Wende die Expertenmuster an, die eine echte Codebasis absichern: simulierte Nominalität, Prüfung ohne Verbreiterung und ambiente Deklarationen.
Diese Lektion in Kodokon öffnenDie Typisierung von TypeScript ist strukturell: Zwei Typen mit derselben Form sind austauschbar, eine Nutzer-ID vom Typ string ist also von jedem anderen string nicht zu unterscheiden. Branding simuliert eine nominale Typisierung, indem der Basistyp mit einer Phantom-Eigenschaft geschnitten wird, deren Schlüssel ein unique symbol ist: Kein echter Wert besitzt sie jemals, aber der Compiler hält nun jede Marke auseinander.
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"); // error: bare string rejectedDer Operator satisfies (TypeScript 4.9) schließt eine ganz bestimmte Lücke. Eine Annotation : T verbreitert den Typ der Variable auf T und verliert dabei die abgeleiteten Literale; ein as T schaltet einen Teil der Prüfungen ab. satisfies T überprüft, dass der Ausdruck zu T passt, ohne den abgeleiteten Typ anzutasten, der so genau wie möglich bleibt. Es ist das ideale Werkzeug für Konfigurationsobjekte: vollständige Prüfung, Genauigkeit intakt.
type RouteDef = { path: string; auth?: boolean };
const routes = {
home: { path: "/" },
admin: { path: "/admin", auth: true },
} satisfies Record<string, RouteDef>;
routes.admin.auth;
// literal type true: the inferred precision is
// preserved, and an unknown key would be rejectedEine .d.ts-Datei enthält ausschließlich ambiente Deklarationen: Sie beschreibt Werte, die zur Laufzeit existieren werden, ohne eine einzige Zeile JavaScript zu erzeugen. Um den globalen Geltungsbereich aus einem Modul heraus zu erweitern, ist der Block declare global Pflicht - und die Spezifikation erlaubt ihn nur in einer Datei, die tatsächlich ein Modul ist, daher das Idiom export {} am Anfang der Datei.
export {};
declare global {
interface Window {
analytics: { track(event: string): void };
}
}Für eine JavaScript-Abhängigkeit ohne veröffentlichte Typen erzeugt declare module ein ambientes Modul: Der Compiler verwendet deine Deklaration für jeden Import des Pakets. Achte auf die Reichweite von skipLibCheck: Ist es aktiviert, ignoriert es die Fehler aller .d.ts-Dateien, auch deiner eigenen; überprüfe deine Deklarationen also mit Typtests, die in gewöhnlichen .ts-Dateien liegen.
declare module "legacy-lib" {
export interface InitOptions {
debug?: boolean;
}
export function init(options?: InitOptions): void;
}satisfies T und einer Annotation : T?Brand<string, ...> gebauter markierter Wert?export {} am Anfang einer .d.ts-Datei hinzu, die declare global verwendet?