Modules et namespaces
Modules ES dans TypeScript
TypeScript utilise le même système de modules que JavaScript moderne : les modules ES. Chaque fichier est un module isolé. Tu choisis ce que tu exportes et ce que tu importes.
Il y a deux types d'exports :
- Export nommé (
export) — tu peux en avoir plusieurs par fichier. - Export par défaut (
export default) — un seul par fichier, importé sans accolades.
// ── math.ts ──────────────────────────────────
// Exports nommés
export function add(a: number, b: number): number {
return a + b;
}
export const PI = 3.14159;
// Export par défaut
export default function multiply(a: number, b: number): number {
return a * b;
}
// ── main.ts ──────────────────────────────────
// Import nommé — accolades obligatoires
import { add, PI } from './math';
// Import par défaut — pas d'accolades, nom libre
import multiply from './math';
// Import tout dans un namespace local
import * as Math from './math';
console.log(add(2, 3)); // 5
console.log(PI); // 3.14159
console.log(multiply(4, 5)); // 20
console.log(Math.add(1, 1)); // 2
Re-exporter depuis un module
Tu peux re-exporter les exports d'un autre fichier directement, sans passer par une variable intermédiaire. C'est la base des barrel files (abordés plus bas).
// ── index.ts ─────────────────────────────────
// Re-exporter tel quel
export { add, PI } from './math';
// Re-exporter avec un alias
export { add as addNumbers } from './math';
// Re-exporter tout
export * from './math';
// Re-exporter l'export par défaut comme export nommé
export { default as multiply } from './math';
Importer des types avec import type
TypeScript permet d'importer uniquement des types avec import type. Ces imports sont effacés à la compilation — aucun code JavaScript n'est généré. C'est plus performant et plus explicite.
// ── types.ts ─────────────────────────────────
export interface User {
id: number;
name: string;
email: string;
}
export type Role = 'admin' | 'editor' | 'viewer';
// ── service.ts ───────────────────────────────
// import type = effacé à la compilation, 0 impact runtime
import type { User, Role } from './types';
function getUser(id: number): User {
return { id, name: 'Alice', email: 'alice@example.com' };
}
function canEdit(role: Role): boolean {
return role === 'admin' || role === 'editor';
}
Règle simple : si tu n'utilises que le symbole comme annotation de type (pas comme valeur), utilise import type. Ton compilateur te le rappellera si tu l'oublies avec l'option verbatimModuleSyntax.
Fichiers de déclaration .d.ts
Un fichier .d.ts est un fichier de déclaration. Il décrit les types d'un module JavaScript existant, sans contenir de code exécutable. TypeScript s'en sert pour typer des bibliothèques écrites en JS pur.
// ── legacy-lib.d.ts ──────────────────────────
// Déclarer une bibliothèque JS existante
declare module 'legacy-lib' {
export function parse(input: string): object;
export function stringify(data: object): string;
export const VERSION: string;
}
// Déclarer une variable globale injectée par l'environnement
declare const __APP_VERSION__: string;
// Augmenter un type existant (module augmentation)
declare module 'express' {
interface Request {
user?: User;
}
}
TypeScript génère automatiquement des .d.ts quand tu actives "declaration": true dans tsconfig.json. Tu distribues alors ton package avec ses types intégrés.
@types — DefinitelyTyped
La plupart des bibliothèques populaires n'ont pas de types intégrés. Le projet DefinitelyTyped maintient des déclarations de types communautaires, installables via le préfixe @types/.
# Installer les types pour Node.js
npm install --save-dev @types/node
# Installer les types pour Express
npm install --save-dev @types/express
# Installer les types pour Jest
npm install --save-dev @types/jest
// Après npm install @types/node, ces imports sont typés
import * as fs from 'fs';
import * as path from 'path';
const content = fs.readFileSync('data.txt', 'utf8');
// TypeScript connaît le type de retour : string
Quand tu installes une bibliothèque et que TypeScript se plaint de "Could not find a declaration file", la première chose à essayer est npm install --save-dev @types/nom-du-package. Si le package n'existe pas, il faudra écrire un .d.ts local.
Namespaces TypeScript
Les namespaces sont une feature spécifique à TypeScript, antérieure aux modules ES. Ils permettent de regrouper des types et du code sous un nom commun pour éviter les collisions.
// Namespace : regroupe des types et des fonctions liés
namespace Utils {
export interface Config {
debug: boolean;
version: string;
}
export function log(msg: string): void {
console.log(`[Utils] ${msg}`);
}
// Namespace imbriqué
export namespace Validation {
export function isEmail(s: string): boolean {
return s.includes('@');
}
}
}
// Utilisation
Utils.log('démarrage');
const ok = Utils.Validation.isEmail('alice@example.com'); // true
const cfg: Utils.Config = { debug: true, version: '1.0' };
Aujourd'hui, les namespaces sont surtout utiles dans les fichiers de déclaration .d.ts pour décrire des bibliothèques avec des globals, ou dans du code TypeScript compilé en un seul fichier. Pour les nouveaux projets, préfère toujours les modules ES.
Alias de chemins avec paths
Dans les grands projets, les imports relatifs deviennent illisibles : ../../../utils/format. La configuration paths dans tsconfig.json permet de créer des alias clairs.
// ── tsconfig.json ────────────────────────────
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@utils/*": ["src/utils/*"],
"@models/*": ["src/models/*"],
"@config": ["src/config/index.ts"]
}
}
}
// Avant les alias — illisible
import { formatDate } from '../../../utils/date';
import type { User } from '../../models/user';
// Après les alias — clair et stable
import { formatDate } from '@utils/date';
import type { User } from '@models/user';
Attention : TypeScript résout les alias à la compilation, mais le runtime (Node.js, bundler) ne les connaît pas. Il faut aussi configurer ton bundler (Vite, Webpack…) ou utiliser tsconfig-paths avec ts-node.
Barrel files — centraliser les exports
Un barrel file est un fichier index.ts qui re-exporte tout ce qu'un dossier expose publiquement. Il crée un point d'entrée unique pour un module.
// Structure :
// src/
// models/
// user.ts
// product.ts
// order.ts
// index.ts ← barrel file
// ── src/models/index.ts ──────────────────────
export * from './user';
export * from './product';
export * from './order';
// Sans barrel file — un import par fichier
import type { User } from './models/user';
import type { Product } from './models/product';
import type { Order } from './models/order';
// Avec barrel file — un seul import propre
import type { User, Product, Order } from './models';
export/import— système de modules ES, chaque fichier est isolé- Export nommé : accolades à l'import. Export par défaut : pas d'accolades, nom libre.
export { ... } from './module'— re-exporter sans import intermédiaireimport type— importer uniquement des types, effacé à la compilation.d.ts— fichier de déclaration pour typer du code JavaScript tiers@types/package— types communautaires via DefinitelyTypednamespace— utile dans les.d.ts, à éviter dans le nouveau code au profit des modules ESpathsdanstsconfig.json— alias pour simplifier les imports profonds- Barrel file (
index.ts) — centraliser les exports d'un dossier en un point d'entrée