Appliquez les patterns experts qui sécurisent une base de code réelle : nominalité simulée, validation sans élargissement et déclarations ambiantes.
Ouvrir cette leçon dans KodokonLe typage de TypeScript est structurel : deux types de même forme sont interchangeables, si bien qu'un string d'identifiant utilisateur se confond avec n'importe quel autre string. Le branding simule un typage nominal en intersectant le type de base avec une propriété fantôme dont la clé est un unique symbol : aucune valeur réelle ne la possède, mais le compilateur, lui, distingue désormais chaque marque.
declare const brand: unique symbol;
type Brand<T, Name extends string> =
T & { readonly [brand]: Name };
type UserId = Brand<string, "UserId">;
type OrderId = Brand<string, "OrderId">;
function asUserId(raw: string): UserId {
return raw as UserId;
}
declare function loadUser(id: UserId): void;
loadUser(asUserId("u_42")); // OK
// loadUser("u_42"); // erreur : string nu refuseL'opérateur satisfies (TypeScript 4.9) comble un vide précis. Une annotation : T élargit le type de la variable vers T et fait perdre les littéraux inférés ; un as T désactive une partie des vérifications. satisfies T vérifie la conformité de l'expression à T sans toucher au type inféré, qui reste le plus précis possible. C'est l'outil idéal pour les objets de configuration : validation complète, précision intacte.
type RouteDef = { path: string; auth?: boolean };
const routes = {
home: { path: "/" },
admin: { path: "/admin", auth: true },
} satisfies Record<string, RouteDef>;
routes.admin.auth;
// type litteral true : la precision inferee est
// conservee, et une cle inconnue serait refuseeUn fichier .d.ts contient exclusivement des déclarations ambiantes : il décrit des valeurs qui existeront à l'exécution, sans émettre le moindre JavaScript. Pour étendre le scope global depuis un module, le bloc declare global est obligatoire - et la spécification ne l'autorise que dans un fichier qui est effectivement un module, d'où l'idiome export {} en tête de fichier.
export {};
declare global {
interface Window {
analytics: { track(event: string): void };
}
}Pour une dépendance JavaScript sans types publiés, declare module crée un module ambiant : le compilateur utilisera votre déclaration à chaque import du paquet. Attention à la portée de skipLibCheck : activé, il ignore les erreurs de tous les fichiers .d.ts, y compris les vôtres ; vérifiez donc vos déclarations via des tests de types placés dans des fichiers .ts ordinaires.
declare module "legacy-lib" {
export interface InitOptions {
debug?: boolean;
}
export function init(options?: InitOptions): void;
}satisfies T et une annotation : T ?Brand<string, ...> ?export {} en tête d'un fichier .d.ts qui utilise declare global ?