Modul di TypeScript: Menata Kode di Banyak File

Pelajari cara memecah kode TypeScript ke banyak file dengan ES module: import/export, named vs default export, mengekspor type, import type, dan cara kerja module resolution.

Diterbitkan 20 September 20269 menit bacaOleh ACY Partner Indonesia
Sampul Modul di TypeScript dengan pernyataan import
300 × 250Slot Iklan TersediaPasang iklan Anda di sini

Saat proyek masih mungil, menaruh semuanya di satu file terasa wajar. Tapi begitu kamu menambah fitur kedua, helper ketiga, lalu type keempat, file tunggal itu berubah jadi tembok kode yang melelahkan untuk di-scroll. Di sinilah modul jadi penyelamat. Modul itu sederhananya sebuah file yang memutuskan apa yang dibagikan ke luar dan apa yang disimpan rapat-rapat untuk dirinya sendiri, dan TypeScript memberi kita alat yang jelas serta mudah ditebak untuk menyambungkan file-file itu.

Di panduan ini kita akan menelusuri cara menata kode TypeScript di beberapa file menggunakan ES module. Kita bahas export dan import, perbedaan antara mengekspor value dan mengekspor type, sintaks khusus import type, named export dibanding default export, serta penjelasan santai tentang bagaimana TypeScript menemukan lokasi sebuah modul. Tidak butuh pengalaman modul sebelumnya.

Apa itu modul sebenarnya

Ini aturan yang menjelaskan hampir segalanya: di TypeScript, file apa pun yang punya minimal satu import atau export di level atas otomatis menjadi modul. Sudah, segitu saja. Sebuah modul punya scope-nya sendiri yang bersifat privat, artinya variabel dan fungsi yang kamu deklarasikan di dalamnya tidak terlihat oleh file lain kecuali kamu sengaja menyodorkannya lewat export.

Bayangkan modul seperti sebuah toko kecil. Sebagian besar kegiatan terjadi di balik meja kasir dan tetap di sana. Toko memilih beberapa barang untuk dipajang di etalase, dan barang etalase itulah yang boleh diambil file lain. Kata kunci export adalah cara kamu menaruh sesuatu di etalase.

// user.ts
export interface User {
  id: number;
  name: string;
  email: string;
}

export function greet(user: User): string {
  return `Halo, ${user.name}!`;
}

// Helper ini TIDAK diekspor, jadi tetap privat untuk file ini.
function normalizeEmail(email: string): string {
  return email.trim().toLowerCase();
}

Pada contoh di atas, User dan greet terlihat oleh seluruh aplikasi. Helper normalizeEmail bersifat privat. File lain bisa memakai User dan greet, tapi sama sekali tidak tahu bahwa normalizeEmail itu ada.

Modul vs skrip

File tanpa import atau export di level atas diperlakukan sebagai skrip biasa, dan variabelnya bocor ke scope global. Menambah satu export saja sudah mengubah file itu menjadi modul utuh dengan scope sendiri. Kalau ragu, ekspor sesuatu.

Named export dan cara mengimpornya

Gaya yang kita pakai di atas disebut named export. Setiap hal yang kamu ekspor punya nama, dan file lain menarik nama-nama itu dengan menyebutkannya di dalam kurung kurawal.

// app.ts
import { User, greet } from "./user";

const jane: User = {
  id: 1,
  name: "Jane Doe",
  email: "jane@example.com",
};

console.log(greet(jane)); // "Halo, Jane Doe!"

Ada beberapa hal yang patut diperhatikan. Path "./user" itu relatif terhadap file saat ini, dan kamu tidak menulis ekstensi .ts. Kamu hanya mengimpor nama yang benar-benar dibutuhkan, sehingga kode tetap jelas dan tooling bisa membuang bagian yang tak terpakai nanti. Nama di kedua sisi juga harus sama persis, karena begitulah TypeScript menghubungkannya.

Kalau sebuah nama terlalu umum atau bentrok dengan yang sudah kamu punya, kamu bisa menamainya ulang saat mengimpor pakai as:

import { greet as greetUser } from "./user";

Kamu juga bisa mengumpulkan semua yang diekspor sebuah modul ke dalam satu objek, yang berguna ketika sebuah file mengekspor banyak utilitas kecil:

// Impor semua named export di bawah satu namespace.
import * as userModule from "./user";

const bob = { id: 2, name: "Bob", email: "bob@example.com" };
console.log(userModule.greet(bob));

Default export

Sebuah modul juga boleh menunjuk satu default export: satu “hal utama” yang menjadi inti file itu. Kamu menulis export default, lalu file yang mengimpor bebas memberi nama apa pun, tanpa kurung kurawal.

// logger.ts
export default function logger(message: string): void {
  console.log(`[LOG] ${message}`);
}
// app.ts
import log from "./logger"; // nama apa pun boleh di sini
log("Server dimulai");

Default dan named export bisa hidup berdampingan dalam satu file. Pola yang umum adalah satu default export untuk fitur utama plus beberapa named export untuk helper terkait.

// api.ts
export default function fetchUser(id: number) {
  /* ... */
}

export const BASE_URL = "https://api.example.com";
export const TIMEOUT = 5000;
import fetchUser, { BASE_URL, TIMEOUT } from "./api";

Jadi yang mana yang sebaiknya dipilih? Ini perbandingan singkatnya.

Aspek Named export Default export
Berapa per file Banyak Satu
Nama saat import Harus sama (atau pakai as) Bebas kamu tentukan
Kejelasan saat rename Konsisten di mana-mana Bisa berbeda antar file
Refactor/autocomplete Lebih mudah untuk tooling Sedikit lebih longgar

Pilihan default yang masuk akal

Banyak tim lebih suka memakai named export hampir di semua tempat. Karena namanya tetap, autocomplete, alat rename, dan “cari semua pemakaian” bekerja lebih andal. Default export sah-sah saja, tapi konsistensi named export biasanya makin terasa manfaatnya seiring proyek membesar.

Mengekspor type dibanding mengekspor value

Di sinilah TypeScript menambahkan sesuatu yang tidak dimiliki JavaScript. Kamu bukan cuma mengekspor fungsi dan variabel, tapi juga type seperti interface, type alias, dan enum. Bagian enaknya, sintaks import/export yang sama menangani keduanya.

// shapes.ts
export type ID = string | number;

export interface Product {
  id: ID;
  title: string;
  price: number;
}

export function formatPrice(p: Product): string {
  return `$${p.price.toFixed(2)}`;
}
// cart.ts
import { Product, ID, formatPrice } from "./shapes";

function addToCart(id: ID, product: Product) {
  console.log(formatPrice(product));
}

Ada model berpikir penting di sini. Type hanya hidup pada saat kompilasi. Ketika TypeScript mengubah kodemu menjadi JavaScript, setiap anotasi type, interface, dan import yang khusus type langsung lenyap. Browser tidak pernah melihat Product. Sebaliknya, sebuah value seperti formatPrice adalah kode sungguhan yang ikut bertahan ke JavaScript akhir dan benar-benar dijalankan.

Sintaks import type

Karena type hilang saat kompilasi, TypeScript menyediakan cara untuk bilang “aku mengimpor ini murni sebagai type” lewat import type. Ini membuat maksudmu eksplisit dan menjamin import tersebut tidak menyumbang JavaScript sama sekali pada hasil akhir.

import type { Product } from "./shapes";
import { formatPrice } from "./shapes";

function describe(product: Product): string {
  return formatPrice(product);
}

Kamu juga bisa menandai nama tertentu secara inline dengan modifier type, sehingga semuanya tetap dalam satu baris:

import { type Product, formatPrice } from "./shapes";

Untuk apa repot begini? Ada dua alasan. Pertama, ini mendokumentasikan perbedaannya dengan jelas: siapa pun yang membaca file langsung tahu mana import yang sekadar bentuk dan mana yang berupa kode jalan. Kedua, ini menghilangkan keraguan bagi compiler dan bundler soal apakah sebuah import punya biaya saat runtime, sehingga mencegah sekelompok masalah build yang halus.

Type dihapus, bukan opsional

Memakai import biasa untuk sebuah type juga berhasil, jadi import type lebih merupakan pilihan demi kejelasan dan keamanan, bukan keharusan mutlak. Tapi begitu sebuah proyek mengaktifkan pengaturan yang lebih ketat seputar import khusus type, bersikap eksplisit dengan import type menyelamatkanmu dari error yang membingungkan. Membiasakannya sejak awal adalah langkah bijak.

Ada pula pasangannya, export type, untuk saat kamu ingin membagikan ulang sebuah type tanpa menyeret kode runtime apa pun:

export type { Product, ID } from "./shapes";

Re-export untuk membangun API publik yang rapi

Saat folder makin penuh, mengimpor dari belasan path yang dalam jadi berisik. Cara merapikan yang umum adalah satu file “barrel” yang mengekspor ulang bagian-bagian penting, sehingga sisa aplikasi cukup mengimpor dari satu lokasi yang ramah.

// models/index.ts
export { greet } from "./user";
export type { User } from "./user";
export type { Product, ID } from "./shapes";
export { formatPrice } from "./shapes";
// di tempat lain
import { greet, formatPrice } from "./models";
import type { User, Product } from "./models";

Folder itu kini punya satu pintu depan yang jelas. Pemanggil tidak perlu tahu susunan file di dalamnya, dan kamu bebas menata ulang file di balik barrel tanpa merusak import semua orang.

Bagaimana TypeScript menemukan modul

Ketika TypeScript melihat import { greet } from "./user", ia harus mencari tahu file asli mana yang ditunjuk. Prosesnya disebut module resolution, dan kamu bisa membayangkannya dalam dua kasus besar.

import "./user"        -> RELATIF: lihat tepat di sebelah file ini
import "lodash"        -> BARE: lihat di dalam node_modules

Import relatif diawali ./ atau ../. Keduanya dicari berdasarkan folder file itu sendiri, mirip menyusuri direktori. ./user berarti “file user yang ada di sebelahku”, sedangkan ../shared/config berarti “naik satu level, lalu masuk ke shared”. TypeScript mencoba kandidat yang masuk akal seperti user.ts, user.tsx, user.d.ts, dan folder bernama user yang berisi file index.

Import bare tidak punya titik di depan, seperti import express from "express". Ini adalah nama paket. TypeScript mencarinya di node_modules, dan untuk informasi type ia memeriksa type bawaan paket itu atau paket @types/... yang cocok.

Biasanya kamu tidak mengatur ini satu per satu per file. Sebagai gantinya, tsconfig.json proyek menyimpan beberapa pengaturan yang mengarahkan resolusi, seperti sistem modul apa yang dipakai dan seberapa luas pencariannya. Nama flag persisnya berbeda-beda antar versi TypeScript, jadi inti aman yang perlu diingat bersifat konseptual: path relatif menunjuk ke file milikmu sendiri, nama bare menunjuk ke paket yang terpasang, dan tsconfig.json adalah tombol pengatur detailnya. Saat sebuah import tidak ketemu, hal pertama yang patut dicek adalah ejaan path-nya dan apakah paket (beserta type-nya) memang sudah terpasang.

Kalau kamu sudah paham modul JavaScript

Sistem import/export di TypeScript sama dengan ES module yang mungkin sudah kamu temui di JavaScript, hanya ditambah dukungan type. Kalau kamu pernah memakai modul di JS, hampir semua yang di sini akan terasa akrab, dan satu-satunya bagian yang benar-benar baru adalah mengekspor type serta sintaks import type.

Rangkuman

Modul memungkinkan kamu memecah proyek TypeScript menjadi file-file fokus yang masing-masing memutuskan apa yang dibagikan. Intinya:

  • Sebuah file menjadi modul begitu ia punya import atau export di level atas, dan semua isinya tetap privat sampai diekspor.
  • Named export membawa nama tetap dan biasanya paling cocok dengan tooling; default export adalah satu “hal utama” per file dan bisa diimpor dengan nama apa pun.
  • TypeScript mengekspor type (interface, type alias, enum) dengan sintaks yang sama seperti value, tapi type dihapus saat kompilasi sedangkan value menjadi JavaScript sungguhan.
  • import type dan export type membuat import khusus type jadi eksplisit, mendokumentasikan maksud sekaligus menjamin tanpa biaya runtime.
  • File barrel mengekspor ulang bagian penting sebuah folder di balik satu path import yang rapi.
  • Module resolution intinya: path relatif (./) menunjuk ke file milikmu, nama bare menunjuk ke paket di node_modules, dan tsconfig.json menyetel detailnya.

Mulailah dengan hanya mengekspor apa yang benar-benar dibutuhkan file lain, andalkan named export demi konsistensi, dan gunakan import type saat kamu menarik sebuah bentuk alih-alih kode jalan. Dengan kebiasaan itu, bahkan basis kode TypeScript yang besar tetap nyaman dijelajahi.

Tag:typescriptmodulimportexportfrontend
728 × 90Slot Iklan TersediaPasang iklan Anda di sini

Artikel Terkait

Lihat Semua Artikel

Artikel yang Mungkin Kamu Suka

Apa Itu Framework Frontend? — chip kode dengan tag komponen
Frontend / Dasar

Apa Itu Framework Frontend?

Penjelasan ramah untuk pemula tentang apa itu framework frontend, kenapa mereka ada, masalah apa yang mereka selesaikan dibanding JavaScript biasa, dan kapan kamu memang sebaiknya memakainya.

20 Sep 20268 menit baca
Sampul fundamentals frontend bertuliskan Apa Itu Frontend dan HTML + CSS + JS
Frontend / Dasar

Apa Itu Frontend Development?

Panduan ramah pemula tentang frontend development: bagian website yang kamu lihat dan klik secara langsung. Pahami cara kerja HTML, CSS, dan JavaScript, serta bedanya frontend dengan backend.

20 Sep 20268 menit baca
Ilustrasi sampul untuk Apa yang Dikerjakan Frontend Developer
Frontend / Dasar

Apa yang Dikerjakan Frontend Developer: Panduan untuk Pemula

Penjelasan ramah pemula tentang apa yang sebenarnya dikerjakan frontend developer setiap hari: mengubah desain jadi antarmuka yang berfungsi, membangun UI yang bisa dipakai ulang, sampai urusan responsif, aksesibilitas, dan kecepatan.

20 Sep 20269 menit baca
Kartu judul bertuliskan Styling dan Interaktivitas di atas latar biru gelap ACY Partner
Frontend / Dasar

Cara Kerja Styling dan Interaktivitas

Penjelasan ramah pemula soal bagaimana CSS menyulap HTML polos jadi tampilan rapi, dan bagaimana JavaScript menambahkan perilaku — pola berpikir struktur, gaya, perilaku saat membangun halaman web.

20 Sep 20268 menit baca
Tiga lapisan front-end menyatu di satu halaman web
Frontend / Dasar

Cara HTML, CSS, dan JavaScript Bekerja Bersama

Panduan praktis untuk pemula: bikin satu tombol kecil, beri struktur dengan HTML, tampilan dengan CSS, dan perilaku dengan JavaScript, lalu lihat ketiga lapisan ini bekerja sama di halaman nyata.

20 Sep 20268 menit baca
Sampul ikhtisar praktik terbaik frontend
Frontend / Dasar

Ikhtisar Praktik Terbaik Frontend

Peta ringkas tentang apa itu frontend yang baik — HTML bermakna, CSS rapi, JavaScript secukupnya, desain responsif, performa, dan progressive enhancement, lengkap dengan arahan untuk mendalaminya.

20 Sep 20268 menit baca