Menu

Namespace di TypeScript: Apa Itu dan Kapan Memakainya

Namespace TypeScript mengelompokkan nilai dan tipe di bawah satu nama dan dikompilasi menjadi object biasa. Pelajari sintaksnya, bagaimana namespace bergabung satu sama lain serta dengan fungsi dan class, kenapa ES module menggantikannya, dan di mana kamu masih menemuinya: declaration file dan global augmentation.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

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.

NamespaceModule
Unitblok bernama di dalam filefile itu sendiri
Scopeglobal, kecuali berada di dalam moduleselalu punya scope sendiri
Dependencyimplisit, berdasarkan urutan pemuatanimport yang eksplisit
Outputobject yang dibangun oleh sebuah fungsiimport/export atau require
Pembuangan kode yang tidak dipakaibundler mempertahankan setiap memberbundler bisa membuang export yang tidak dipakai
Berjalan dengan type stripping Nodehanya kalau isinya tipe sajaya

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.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai