Namespace adalah blok bernama yang mengelompokkan fungsi, konstanta, dan tipe di bawah satu nama. Member yang ditandai export bisa diakses sebagai Name.member; sisanya tetap privat di dalam blok itu. Saat runtime, namespace adalah object biasa.
Namespace Dikompilasi Menjadi Apa
Namespace adalah salah satu dari sedikit fitur TypeScript yang menghasilkan kode. Blok di atas menjadi sebuah fungsi yang mengisi sebuah object:
var Geometry;
(function (Geometry) {
const TAU = Math.PI * 2;
function circumference(radius) {
return TAU * radius;
}
Geometry.circumference = circumference;
function area(radius) {
return Math.PI * radius ** 2;
}
Geometry.area = area;
})(Geometry || (Geometry = {}));
Inilah pola yang dipakai kode JavaScript sebelum ada module, agar tidak semua nama ditaruh di global scope. TAU adalah variabel lokal fungsi tersebut, dan karena itulah kode lain tidak bisa melihatnya. Mencoba membacanya adalah error saat kompilasi:
Compiler melaporkan index.ts(10,17): error TS2339: Property 'rate' does not exist on type 'typeof Tax'. Tambahkan export di depan const rate, dan kedua baris itu akan berjalan.
Bersarang, Merging, dan Alias
Namespace bisa bersarang, dan dua blok dengan nama yang sama akan digabung menjadi satu. Geometry || (Geometry = {}) di output-lah yang membuatnya bekerja: blok kedua menambahkan isi ke object yang sudah ada. import X = A.B membuat alias yang lebih pendek.
namespace A.B.C { } adalah singkatan untuk tiga blok bersarang. Penulisan lama module Shop { } artinya sama, tetapi sekarang ditolak dengan error TS1540, A 'namespace' declaration should not be declared using the 'module' keyword. Please use the 'namespace' keyword instead.
Merging dengan Fungsi dan Class
Namespace bisa memakai nama yang sama dengan fungsi, class, atau enum, lalu menambahkan member ke dalamnya. Ini masih cara paling rapi untuk menjelaskan fungsi yang juga membawa properti, atau class yang ditempeli helper.
Untuk class, method static melakukan tugas yang sama dan merupakan JavaScript biasa. Untuk fungsi, kamu juga bisa melewatkan namespace dan langsung meng-assign format.prefix = "$" tepat setelah deklarasinya; TypeScript melacak properti yang di-assign dengan cara itu.
Namespace vs Module
Sebelum ada ES module, program TypeScript yang besar terdiri dari banyak file script yang berbagi namespace global, disatukan dengan /// <reference path="..." /> dan dikompilasi menjadi satu file dengan outFile. Module menggantikan cara itu: setiap file punya scope sendiri, dependency berupa import yang eksplisit, dan bundler bisa membuang export yang tidak dipakai. TypeScript 7 menghapus outFile (error TS5102), jadi susunan namespace multi-file tidak lagi menjadi pilihan build.
| Namespace | Module | |
|---|---|---|
| Unit | blok bernama di dalam file | file itu sendiri |
| Scope | global, kecuali berada di dalam module | selalu punya scope sendiri |
| Dependency | implisit, berdasarkan urutan pemuatan | import yang eksplisit |
| Output | object yang dibangun oleh sebuah fungsi | import/export atau require |
| Pembuangan kode yang tidak dipakai | bundler mempertahankan setiap member | bundler bisa membuang export yang tidak dipakai |
| Berjalan dengan type stripping Node | hanya kalau isinya tipe saja | ya |
Di dalam sebuah module, membungkus semuanya dengan namespace hanya menambah level penamaan kedua tanpa manfaat: file yang meng-import harus menulis Utils.Utils.format. Export fungsinya secara langsung, dan biarkan file yang meng-import memilih import * as Utils from "./utils.js" kalau mereka ingin prefix.
Di Mana Kamu Masih Menemui Namespace
Declaration file memakainya untuk menjelaskan library yang menyediakan satu object global, dan untuk mengelompokkan tipe:
// jquery-like.d.ts: a global function that also has properties
declare function $(selector: string): unknown;
declare namespace $ {
const version: string;
function ajax(url: string): Promise<unknown>;
}
Package tipe memakainya untuk menyediakan tipe yang bisa kamu perluas. @types/node mendeklarasikan namespace NodeJS, dan menambahkan isi ke interface ProcessEnv-nya dari module mana pun membutuhkan declare global:
// env.d.ts
export {};
declare global {
namespace NodeJS {
interface ProcessEnv {
API_URL: string; // process.env.API_URL is now string, not string | undefined
}
}
}
Interface di dalam namespace yang di-merge juga ikut di-merge, dan itulah yang membuat augmentation ini bekerja.
Namespace dan Type Stripping
Node 24 menjalankan file .ts dengan menghapus sintaks tipe. Namespace yang berisi nilai tidak bisa dihapus begitu saja, karena harus dikompilasi menjadi object seperti yang ditunjukkan di atas, jadi node app.ts berhenti dengan:
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript namespace declaration is not supported in strip-only mode
Ada dua jenis namespace yang aman karena hilang sepenuhnya: declare namespace, dan namespace yang semua member-nya berupa tipe atau interface. Opsi compiler erasableSyntaxOnly: true melaporkan jenis lainnya saat kompilasi sebagai error TS1294, This syntax is not allowed when 'erasableSyntaxOnly' is enabled., dan begitulah proyek yang berjalan dengan type stripping menjauhkannya. node --experimental-transform-types memang mengompilasi namespace, dengan peringatan eksperimental.
Pertanyaan yang sering diajukan
Sebaiknya pakai namespace atau module di TypeScript?
Pakai module (import dan export) untuk kode baru. Setiap file sudah punya scope sendiri, bundler dan Node memahami module, dan export yang tidak dipakai bisa dibuang. Namespace tetap berguna di declaration file, untuk global augmentation, dan untuk menempelkan tipe atau helper pada fungsi atau class yang namanya sama.
Namespace TypeScript dikompilasi menjadi apa?
Object yang diisi oleh fungsi yang langsung dipanggil: var Geometry; (function (Geometry) { Geometry.circle = circle; })(Geometry || (Geometry = {}));. Member yang di-export menjadi properti object itu; member tanpa export tetap lokal di dalam fungsi.
Apa perbedaan namespace dan module di TypeScript?
Module adalah file yang punya import atau export di level teratas. Namespace adalah blok bernama di dalam sebuah file. TypeScript versi lama menyebut namespace sebagai "internal module" dan mengizinkan module Foo {}; penulisan itu sekarang menghasilkan error TS1540, dan hanya namespace Foo {} yang diterima.
Bisakah Node menjalankan file TypeScript yang memakai namespace?
Tidak dengan type stripping bawaannya. Namespace yang berisi nilai menghasilkan ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX, karena stripping hanya menghapus tipe, padahal namespace butuh kode yang di-generate. Namespace yang hanya berisi tipe, dan declare namespace, dihapus seluruhnya dan bisa berjalan tanpa masalah.