Menu

Namespace en TypeScript : définition et cas d'usage

Un namespace TypeScript regroupe des valeurs et des types sous un même nom et se compile en un simple objet. Découvrez la syntaxe, comment les namespaces fusionnent entre eux et avec les fonctions et les classes, pourquoi les modules ES les ont remplacés, et où vous les rencontrez encore : fichiers de déclaration et augmentation globale.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Un namespace est un bloc nommé qui regroupe des fonctions, des constantes et des types sous un même nom. Les membres marqués export sont accessibles via Name.member ; les autres restent privés au bloc. À l'exécution, un namespace est un simple objet.

En quoi un namespace est compilé

Les namespaces sont l'une des rares fonctionnalités de TypeScript qui génèrent du code. Le bloc ci-dessus devient une fonction qui remplit un objet :

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 = {}));

C'est le pattern qu'utilisait le code JavaScript avant l'arrivée des modules pour éviter de mettre chaque nom dans la portée globale. TAU est une variable locale de la fonction, c'est pourquoi le reste du code ne peut pas la voir. Essayer de la lire est une erreur de compilation :

Le compilateur signale index.ts(10,17): error TS2339: Property 'rate' does not exist on type 'typeof Tax'. Ajoutez export devant const rate et les deux lignes s'exécutent.

Imbrication, fusion et alias

Les namespaces peuvent s'imbriquer, et deux blocs du même nom fusionnent en un seul. C'est le Geometry || (Geometry = {}) de la sortie qui rend cela possible : le second bloc ajoute des membres à l'objet existant. import X = A.B crée un alias court.

namespace A.B.C { } est un raccourci pour trois blocs imbriqués. L'ancienne écriture module Shop { } veut dire la même chose mais elle est désormais refusée avec l'erreur TS1540, A 'namespace' declaration should not be declared using the 'module' keyword. Please use the 'namespace' keyword instead.

Fusion avec les fonctions et les classes

Un namespace peut porter le même nom qu'une fonction, une classe ou un enum et lui ajouter des membres. C'est encore la façon la plus propre de décrire une fonction qui porte aussi des propriétés, ou une classe accompagnée de fonctions utilitaires.

Pour une classe, une méthode static fait le même travail en JavaScript pur. Pour une fonction, vous pouvez aussi vous passer du namespace et affecter format.prefix = "$" juste après la déclaration ; TypeScript suit les propriétés affectées de cette façon.

Namespaces ou modules

Avant les modules ES, un gros programme TypeScript était composé de nombreux fichiers de script qui partageaient des namespaces globaux, reliés par /// <reference path="..." /> et compilés en un seul fichier avec outFile. Les modules ont remplacé cela : chaque fichier a sa propre portée, les dépendances sont des imports explicites, et les bundlers peuvent supprimer les exports inutilisés. TypeScript 7 a retiré outFile (erreur TS5102), donc la configuration en namespaces sur plusieurs fichiers n'est plus une option de build.

NamespaceModule
Unitéun bloc nommé dans un fichierle fichier lui-même
Portéeglobale, sauf dans un moduletoujours la sienne
Dépendancesimplicites, par ordre de chargementimport explicite
Sortieun objet construit par une fonctionimport/export ou require
Suppression du code inutiliséles bundlers gardent chaque membreles bundlers peuvent supprimer les exports inutilisés
Fonctionne avec le type stripping de Nodeseulement s'il ne contient que des typesoui

Dans un module, tout envelopper dans un namespace ajoute un second niveau de nommage sans aucun gain : les fichiers qui importent écriraient Utils.Utils.format. Exportez directement les fonctions et laissez celui qui importe choisir import * as Utils from "./utils.js" s'il veut un préfixe.

Où l'on rencontre encore des namespaces

Les fichiers de déclaration les utilisent pour décrire des bibliothèques qui exposent un seul objet global, et pour regrouper des types :

// 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>;
}

Les paquets de types les utilisent pour exposer des types que vous pouvez étendre. @types/node déclare namespace NodeJS, et compléter son interface ProcessEnv depuis un module demande 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
        }
    }
}

Les interfaces à l'intérieur de namespaces fusionnés fusionnent elles aussi, et c'est ce qui fait fonctionner cette augmentation.

Namespaces et type stripping

Node 24 exécute les fichiers .ts en supprimant la syntaxe de type. Un namespace qui contient des valeurs ne peut pas être supprimé, il doit être compilé en l'objet montré plus haut, donc node app.ts s'arrête avec :

SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript namespace declaration is not supported in strip-only mode

Deux sortes de namespaces ne posent pas de problème, car elles disparaissent entièrement : un declare namespace, et un namespace dont tous les membres sont des types ou des interfaces. L'option de compilation erasableSyntaxOnly: true signale les autres à la compilation avec l'erreur TS1294, This syntax is not allowed when 'erasableSyntaxOnly' is enabled., et c'est ainsi que les projets qui tournent en type stripping les tiennent à l'écart. node --experimental-transform-types compile bien les namespaces, avec un avertissement expérimental.

Questions fréquentes

Faut-il utiliser des namespaces ou des modules en TypeScript ?

Utilisez des modules (import et export) pour le nouveau code. Chaque fichier a déjà sa propre portée, les bundlers et Node comprennent les modules, et les exports inutilisés peuvent être supprimés. Les namespaces restent utiles dans les fichiers de déclaration, pour l'augmentation globale, et pour rattacher des types ou des fonctions utilitaires à une fonction ou une classe du même nom.

En quoi un namespace TypeScript est-il compilé ?

En un objet rempli par une fonction immédiatement invoquée : var Geometry; (function (Geometry) { Geometry.circle = circle; })(Geometry || (Geometry = {}));. Les membres exportés deviennent des propriétés de cet objet ; les membres sans export restent locaux à la fonction.

Quelle est la différence entre namespace et module en TypeScript ?

Un module est un fichier qui contient un import ou un export de premier niveau. Un namespace est un bloc nommé à l'intérieur d'un fichier. L'ancien TypeScript appelait les namespaces « modules internes » et acceptait module Foo {} ; cette écriture provoque aujourd'hui l'erreur TS1540, et seul namespace Foo {} est accepté.

Node peut-il exécuter des fichiers TypeScript qui utilisent des namespaces ?

Pas avec son type stripping par défaut. Un namespace qui contient des valeurs produit ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX, car le stripping se contente de supprimer les types alors qu'un namespace demande du code généré. Les namespaces qui ne contiennent que des types, et declare namespace, sont effacés et s'exécutent sans problème.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER