Langsung ke konten
KamusNgoding
Menengah Typescript 5 menit baca

CommonJS vs ES Modules di TypeScript: Perbedaan dan Kapan Menggunakannya

#typescript #modules #commonjs #es modules #intermediate

Konsep Dasar

Apa itu CommonJS?

CommonJS (disingkat CJS) adalah sistem modul yang pertama kali diperkenalkan untuk Node.js. Sistem ini menggunakan sintaks require() untuk mengimpor modul dan module.exports atau exports untuk mengekspor.

CommonJS dirancang untuk lingkungan server-side (Node.js) dan bersifat sinkron — artinya, ketika kamu memanggil require(), Node.js akan langsung membaca dan mengeksekusi file tersebut sebelum melanjutkan ke baris berikutnya.

Apa itu ES Modules?

ES Modules (disingkat ESM) adalah sistem modul standar yang resmi diperkenalkan di JavaScript melalui spesifikasi ECMAScript 2015 (ES6). Sistem ini menggunakan sintaks import dan export yang sudah sangat familiar bagi developer modern.

ES Modules dirancang untuk mendukung analisis statis — compiler dan bundler bisa mengetahui dependency antar modul sebelum kode dieksekusi. Ini memungkinkan fitur seperti tree-shaking (menghapus kode yang tidak digunakan) bekerja dengan efektif.

Perbedaan Utama

AspekCommonJSES Modules
Sintaks imporrequire()import
Sintaks ekspormodule.exportsexport
Waktu eksekusiSinkronAsinkron
Tree-shakingTidak didukungDidukung penuh
Top-level awaitTidak didukungDidukung
Lingkungan utamaNode.jsBrowser + Node.js
Ekstensi file.js / .cjs.mjs atau dengan "type": "module"

Contoh Kode

CommonJS di TypeScript

Berikut adalah contoh penggunaan CommonJS dalam proyek TypeScript. Kita buat dua file: satu untuk mendefinisikan fungsi utilitas, satu lagi untuk menggunakannya.

src/mathUtils.ts

// Tipe untuk fungsi operasi matematika
type OperasiMatematika = (a: number, b: number) => number;

// Definisi fungsi-fungsi utilitas
const tambah: OperasiMatematika = (a, b) => a + b;
const kurang: OperasiMatematika = (a, b) => a - b;
const kali: OperasiMatematika = (a, b) => a * b;

// Ekspor semua fungsi dengan gaya CommonJS
module.exports = { tambah, kurang, kali };

src/app.ts

// Impor fungsi dari mathUtils menggunakan require (CommonJS)
const { tambah, kurang, kali } = require('./mathUtils');

// Gunakan fungsi-fungsi tersebut
const a = 10;
const b = 5;

console.log(`Tambah: ${tambah(a, b)}`);
console.log(`Kurang: ${kurang(a, b)}`);
console.log(`Kali:   ${kali(a, b)}`);

/*
Output yang diharapkan:
> Tambah: 15
> Kurang: 5
> Kali:   50
*/

Konfigurasi tsconfig.json untuk CommonJS:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "ES2020",
    "rootDir": "./src",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules", "dist"]
}

Jalankan kompilasi dan eksekusi:

npx tsc
node dist/app.js
Output:
Tambah: 15
Kurang: 5
Kali:   50

Setelah dikompilasi, TypeScript akan menghasilkan file .js yang menggunakan require() dan module.exports secara native.


ES Modules di TypeScript

Sekarang kita lihat cara penggunaan ES Modules dengan contoh yang setara.

src/mathUtils.ts

// Named export: fungsi ini bisa diimpor dengan nama yang sama
export function tambah(a: number, b: number): number {
  return a + b;
}

// Named export lain untuk operasi pengurangan
export function kurang(a: number, b: number): number {
  return a - b;
}

// Default export: satu modul hanya boleh memiliki satu default export
export default function kali(a: number, b: number): number {
  return a * b;
}

src/app.ts

// Di ESM, path import harus memakai ekstensi .js (bukan .ts)
import kali, { tambah, kurang } from './mathUtils.js';

const a = 10;
const b = 5;

console.log(`Tambah: ${tambah(a, b)}`);
console.log(`Kurang: ${kurang(a, b)}`);
console.log(`Kali:   ${kali(a, b)}`);

/*
Output yang diharapkan:
> Tambah: 15
> Kurang: 5
> Kali:   50
*/

Konfigurasi tsconfig.json untuk ES Modules (gunakan NodeNext agar Node.js modern didukung penuh):

{
  "compilerOptions": {
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "target": "ES2020",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "noEmitOnError": true
  },
  "include": ["src/**/*.ts"]
}

Tambahkan "type": "module" di package.json agar Node.js memperlakukan file .js sebagai ES Module:

{
  "name": "belajar-esm",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "build": "tsc",
    "start": "node dist/app.js",
    "dev": "tsx src/app.ts"
  },
  "devDependencies": {
    "typescript": "^5.8.0",
    "tsx": "^4.19.0"
  }
}

Jalankan kompilasi dan eksekusi:

npx tsc
node dist/app.js
Output:
Tambah: 15
Kurang: 5
Kali:   50

Perhatian penting: Saat menggunakan ES Modules di Node.js, kamu wajib menyertakan ekstensi .js pada path impor (meskipun file aslinya .ts). Ini adalah salah satu “gotcha” terbesar yang sering membingungkan developer baru.


Interoperabilitas: Mengimpor CommonJS dari ES Modules

Salah satu tantangan nyata di ekosistem Node.js adalah banyak paket lama masih menggunakan CommonJS. Berikut cara mengimpornya dari file ES Modules:

// Mengimpor modul CommonJS bawaan Node.js dari ES Module
import path from 'node:path';
import { createRequire } from 'node:module';

// Membuat fungsi require agar bisa memuat modul CommonJS dari file ES Module
const require = createRequire(import.meta.url);

// Memuat modul CommonJS dengan require
const os = require('node:os') as typeof import('node:os');

// Menggabungkan folder home user dengan nama file secara aman lintas OS
const filePath = path.join(os.homedir(), 'belajar-typescript.txt');

console.log(`Platform: ${os.platform()}`);
console.log(`Contoh path: ${filePath}`);

/*
Output yang diharapkan (contoh di Windows):
> Platform: win32
> Contoh path: C:\Users\NamaUser\belajar-typescript.txt

Output di Linux/macOS:
> Platform: linux
> Contoh path: /home/namauser/belajar-typescript.txt
*/

Contoh Kasus Nyata: Modul Utilitas Format Harga

Bayangkan kamu sedang membangun layanan backend seperti yang digunakan pada platform e-commerce. Kamu membutuhkan modul utilitas untuk memformat harga dalam Rupiah.

src/priceFormatter.ts (menggunakan ES Modules)

interface FormatOptions {
  currency: string;
  locale: string;
}

// Konfigurasi default untuk format mata uang Indonesia
const defaultOptions: FormatOptions = {
  currency: 'IDR',
  locale: 'id-ID',
};

// Named export untuk fungsi format harga
export function formatRupiah(
  amount: number,
  options: FormatOptions = defaultOptions
): string {
  return new Intl.NumberFormat(options.locale, {
    style: 'currency',
    currency: options.currency,
    minimumFractionDigits: 0,
  }).format(amount);
}

// Named export untuk menghitung diskon
export function formatDiskon(
  hargaAsli: number,
  persen: number
): { hargaDiskon: number; hargaFinal: number } {
  const hargaDiskon = (hargaAsli * persen) / 100;
  return {
    hargaDiskon,
    hargaFinal: hargaAsli - hargaDiskon,
  };
}

// Default export untuk konfigurasi default
export default defaultOptions;

src/checkout.ts

import { formatRupiah, formatDiskon } from './priceFormatter.js';

// Simulasi proses checkout dengan diskon
const hargaProduk = 100_000;
const persenDiskon = 15;

const { hargaDiskon, hargaFinal } = formatDiskon(hargaProduk, persenDiskon);

console.log(`Harga asli  : ${formatRupiah(hargaProduk)}`);
console.log(`Diskon 15%  : -${formatRupiah(hargaDiskon)}`);
console.log(`Harga bayar : ${formatRupiah(hargaFinal)}`);

/*
Output yang diharapkan:
> Harga asli  : Rp100.000
> Diskon 15%  : -Rp15.000
> Harga bayar : Rp85.000
*/

Kapan Menggunakan CommonJS vs ES Modules?

SituasiRekomendasi
Proyek Node.js yang sudah adaTetap di CommonJS, migrasi bisa bertahap
Proyek baru dengan Node.js 18+Gunakan ES Modules (NodeNext)
Library yang akan dipublikasikan ke npmES Modules (banyak ekosistem modern hanya mendukung ESM)
Aplikasi frontend / browserES Modules
Script sederhana / tooling internalCommonJS masih lebih mudah
Butuh top-level awaitWajib ES Modules

Lihat juga Tutorial Konfigurasi tsconfig.json untuk Pemula untuk memahami lebih lanjut opsi-opsi kompilasi TypeScript yang relevan.


Pertanyaan yang Sering Diajukan

Apa perbedaan utama antara CommonJS dan ES Modules?

CommonJS menggunakan require() dan module.exports dengan eksekusi sinkron, sementara ES Modules menggunakan import/export dengan analisis statis yang memungkinkan tree-shaking. CommonJS adalah standar lama Node.js, sedangkan ES Modules adalah standar JavaScript modern yang didukung di browser dan Node.js versi baru.

Mengapa saya harus menuliskan ekstensi .js saat mengimpor di ES Modules?

Ini adalah persyaratan dari spesifikasi ES Modules dan Node.js — resolver modul tidak akan menambahkan ekstensi secara otomatis. Meskipun file sumbermu adalah .ts, TypeScript mengkompilasi ke .js, sehingga path impor harus merujuk ke file output .js. Jika kamu lupa ekstensinya, kamu akan mendapatkan error ERR_MODULE_NOT_FOUND.

Bagaimana cara menggunakan kedua sistem modul sekaligus dalam satu proyek?

Kamu bisa menggunakan file .cjs untuk CommonJS dan .mjs untuk ES Modules secara bersamaan. Alternatifnya, set "type": "module" di package.json untuk membuat ESM sebagai default, lalu gunakan ekstensi .cjs untuk file yang harus tetap CommonJS. TypeScript mendukung ini dengan konfigurasi "module": "NodeNext".

Apakah ES Modules lebih cepat dari CommonJS?

Tidak selalu dalam hal kecepatan eksekusi mentah, tetapi ES Modules memungkinkan tree-shaking yang berarti bundler bisa menghapus kode yang tidak dipakai. Untuk aplikasi besar, ini bisa mengurangi ukuran bundle secara signifikan dan berdampak pada waktu load aplikasi di browser.

Apakah saya perlu migrasi dari CommonJS ke ES Modules?

Tidak harus, terutama untuk proyek Node.js yang sudah berjalan stabil. Migrasi direkomendasikan jika kamu ingin mempublikasikan library ke npm (banyak library modern hanya mendukung ESM), atau jika proyekmu akan digunakan di browser tanpa bundler. Untuk proyek baru, pertimbangkan ES Modules sebagai pilihan default.


Kesimpulan

CommonJS dan ES Modules adalah dua sistem modul yang memiliki peran berbeda dalam ekosistem JavaScript dan TypeScript. CommonJS cocok untuk proyek Node.js yang sudah ada dan butuh kompatibilitas luas, sementara ES Modules adalah pilihan masa depan yang mendukung tree-shaking, top-level await, dan standar web modern.

Kunci utamanya: jika kamu memulai proyek baru dengan Node.js 18+ atau membangun library yang akan dipublikasikan, pilih ES Modules dengan konfigurasi "module": "NodeNext". Untuk proyek yang sudah berjalan atau butuh integrasi cepat dengan banyak paket lama, CommonJS masih merupakan pilihan yang solid.

Selamat bereksperimen — memahami sistem modul adalah fondasi penting untuk menulis kode TypeScript yang lebih terstruktur dan modern. Jika ada pertanyaan, jangan ragu untuk mengeksplorasi artikel-artikel lainnya di KamusNgoding — masih banyak topik seru yang menunggumu!

Artikel Terkait