Namespace to nazwany blok, który grupuje funkcje, stałe i typy pod jedną nazwą. Elementy oznaczone export są dostępne jako Name.member; reszta pozostaje prywatna w bloku. W czasie działania namespace jest zwykłym obiektem.
Do czego kompiluje się namespace
Namespace'y to jedna z niewielu funkcji TypeScript, które generują kod. Powyższy blok staje się funkcją, która wypełnia obiekt:
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 = {}));
To wzorzec, którego kod JavaScript używał, zanim pojawiły się moduły, żeby nie umieszczać każdej nazwy w zakresie globalnym. TAU jest zmienną lokalną funkcji, dlatego inny kod jej nie widzi. Próba odczytania jej to błąd kompilacji:
Kompilator zgłasza index.ts(10,17): error TS2339: Property 'rate' does not exist on type 'typeof Tax'. Dodaj export przed const rate, a obie linie się wykonają.
Zagnieżdżanie, łączenie i aliasy
Namespace'y można zagnieżdżać, a dwa bloki o tej samej nazwie łączą się w jeden. Umożliwia to Geometry || (Geometry = {}) w wyjściu: drugi blok dopisuje do istniejącego obiektu. import X = A.B tworzy krótki alias.
namespace A.B.C { } to skrót od trzech zagnieżdżonych bloków. Dawny zapis module Shop { } znaczy to samo, ale jest teraz odrzucany z błędem TS1540, A 'namespace' declaration should not be declared using the 'module' keyword. Please use the 'namespace' keyword instead.
Łączenie z funkcjami i klasami
Namespace może mieć tę samą nazwę co funkcja, klasa lub enum i dodawać do nich elementy. To wciąż najczystszy sposób na opisanie funkcji, która ma też właściwości, albo klasy z dołączonymi funkcjami pomocniczymi.
W przypadku klasy to samo zadanie spełnia metoda static, która jest zwykłym JavaScriptem. W przypadku funkcji możesz też pominąć namespace i przypisać format.prefix = "$" zaraz po deklaracji; TypeScript śledzi właściwości przypisane w ten sposób.
Namespace'y a moduły
Przed ES modules duży program w TypeScript składał się z wielu plików skryptów dzielących globalne namespace'y, połączonych przez /// <reference path="..." /> i kompilowanych do jednego pliku przez outFile. Zastąpiły to moduły: każdy plik ma własny zakres, zależności to jawne importy, a bundlery mogą usuwać nieużywane eksporty. TypeScript 7 usunął outFile (błąd TS5102), więc konfiguracja z namespace'ami w wielu plikach nie jest już opcją budowania.
| Namespace | Moduł | |
|---|---|---|
| Jednostka | nazwany blok w pliku | sam plik |
| Zakres | globalny, chyba że wewnątrz modułu | zawsze własny |
| Zależności | niejawne, przez kolejność wczytywania | jawny import |
| Wyjście | obiekt budowany przez funkcję | import/export lub require |
| Usuwanie nieużywanego kodu | bundlery zachowują każdy element | bundlery mogą usunąć nieużywane eksporty |
| Działa przy type stripping w Node | tylko jeśli zawiera same typy | tak |
Wewnątrz modułu opakowanie wszystkiego w namespace dodaje drugi poziom nazw bez żadnego zysku: importujący pisałby Utils.Utils.format. Eksportuj funkcje bezpośrednio i pozwól importującemu wybrać import * as Utils from "./utils.js", jeśli chce przedrostka.
Gdzie wciąż spotkasz namespace'y
Pliki deklaracji używają ich do opisu bibliotek, które udostępniają jeden globalny obiekt, i do grupowania typów:
// 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>;
}
Pakiety typów używają ich do udostępniania typów, które można rozszerzać. @types/node deklaruje namespace NodeJS, a dopisanie czegoś do jego interfejsu ProcessEnv z dowolnego modułu wymaga 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
}
}
}
Interfejsy wewnątrz połączonych namespace'ów też się łączą i dzięki temu to rozszerzenie działa.
Namespace'y a type stripping
Node 24 uruchamia pliki .ts, usuwając składnię typów. Namespace z wartościami nie może zostać po prostu usunięty, musi zostać skompilowany do obiektu pokazanego wyżej, więc node app.ts kończy się komunikatem:
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript namespace declaration is not supported in strip-only mode
Dwa rodzaje namespace'ów działają, bo znikają całkowicie: declare namespace oraz namespace, którego wszystkie elementy to typy lub interfejsy. Opcja kompilatora erasableSyntaxOnly: true zgłasza pozostałe już podczas kompilacji jako błąd TS1294, This syntax is not allowed when 'erasableSyntaxOnly' is enabled., i tak projekty działające na type stripping trzymają je z daleka. node --experimental-transform-types kompiluje namespace'y, z ostrzeżeniem o funkcji eksperymentalnej.
Najczęściej zadawane pytania
Używać w TypeScript namespace'ów czy modułów?
W nowym kodzie używaj modułów (import i export). Każdy plik ma już własny zakres, bundlery i Node rozumieją moduły, a nieużywane eksporty można usunąć. Namespace'y wciąż przydają się w plikach deklaracji, przy rozszerzaniu globalnego zakresu i do dołączania typów lub funkcji pomocniczych do funkcji albo klasy o tej samej nazwie.
Do czego kompiluje się namespace w TypeScript?
Do obiektu wypełnianego przez natychmiast wywoływaną funkcję: var Geometry; (function (Geometry) { Geometry.circle = circle; })(Geometry || (Geometry = {}));. Wyeksportowane elementy stają się właściwościami tego obiektu; elementy bez export pozostają lokalne w funkcji.
Czym różni się namespace od modułu w TypeScript?
Moduł to plik z import lub export na najwyższym poziomie. Namespace to nazwany blok wewnątrz pliku. Dawny TypeScript nazywał namespace'y "modułami wewnętrznymi" i pozwalał na module Foo {}; ten zapis jest teraz błędem TS1540 i akceptowane jest tylko namespace Foo {}.
Czy Node uruchomi pliki TypeScript z namespace'ami?
Nie przy domyślnym type stripping. Namespace zawierający wartości daje ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX, bo stripping tylko usuwa typy, a namespace wymaga wygenerowanego kodu. Namespace'y zawierające same typy oraz declare namespace są usuwane i działają bez problemu.