Metaprogrammierung in TypeScript: Wie man Code schreibt, der Code schreibt

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Die Magie, die in den Typen verborgen ist

\n\n

Stellen Sie sich vor, Sie schreiben nicht einfach nur Code, sondern eine Reihe von Anweisungen für den Compiler selbst, der auf der Grundlage Ihrer Regeln sichere, selbst dokumentierende Konstrukte generiert. Das ist Metaprogrammierung in TypeScript – die Fähigkeit, Code zu schreiben, der auf Typebene arbeitet, andere Typen während der Kompilierung analysiert und transformiert.

\n\n

Viele Entwickler bleiben bei grundlegenden Generics und Interfaces stehen, ohne zu ahnen, welch mächtiges Arsenal sich hinter den Schlüsselwörtern infer, extends und bedingten Konstrukten verbirgt. In diesem Artikel werden wir fortgeschrittene Techniken der Metaprogrammierung in TypeScript behandeln, die Sie von einem einfachen Benutzer der Sprache zu ihrem wahren Architekten machen.

\n\n

1. Bedingte Typen: if auf Typebene

\n\n

Bedingte Typen sind die Grundlage jeder Type-Level-Programmierung. Sie funktionieren wie ein ternärer Operator, jedoch auf der Ebene des Typsystems:

\n\n
type IsString<T> = T extends string ? "yes" : "no";\n\ntype Test1 = IsString<"hello">;   // "yes"\ntype Test2 = IsString<42>;        // "no"\n
\n\n

Die wahre Magie beginnt jedoch, wenn wir sie mit Generics kombinieren. Erstellen wir zum Beispiel einen Typ, der nur die String-Schlüssel eines Objekts extrahiert:

\n\n
type StringKeys<T> = {\n  [K in keyof T]: T[K] extends string ? K : never\n}[keyof T];\n\ninterface User {\n  name: string;\n  age: number;\n  email: string;\n}\n\ntype UserStringKeys = StringKeys<User>; // "name" | "email"\n
\n\n

Dieses Beispiel zeigt, wie fortgeschrittene Typen in TypeScript es ermöglichen, Datenstrukturen bereits zur Kompilierungszeit zu filtern und zu transformieren. Sie programmieren buchstäblich eine Logik, die ausgeführt wird, bevor der Code zur Laufzeit gelangt.

\n\n

2. Infer: Typen aus anderen Typen extrahieren

\n\n

Das Schlüsselwort infer ist wohl das mächtigste Werkzeug im Arsenal der Metaprogrammierung in TypeScript. Es erlaubt Ihnen, "in einen Typ hineinzuschauen" und seine Bestandteile zu extrahieren. Betrachten wir ein klassisches Beispiel – das Abrufen des Rückgabetyps einer Funktion:

\n\n
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;\n\ntype Fn = (x: number) => string;\ntype FnReturn = ReturnType<Fn>; // string\n
\n\n

Aber infer kann noch viel mehr. Extrahieren wir den Typ des ersten Arguments einer Funktion:

\n\n
type FirstArg<T> = T extends (first: infer F, ...rest: any[]) => any ? F : never;\n\ntype Handler = (id: number, name: string) => void;\ntype HandlerFirstArg = FirstArg<Handler>; // number\n
\n\n

Und wie wäre es mit einer rekursiven Analyse von Promises? Erstellen wir einen Typ, der alle verschachtelten Promise "entfaltet":

\n\n
type UnwrapPromise<T> = T extends Promise<infer U> ? UnwrapPromise<U> : T;\n\ntype DeepPromise = Promise<Promise<Promise<number>>>;\ntype Unwrapped = UnwrapPromise<DeepPromise>; // number\n
\n\n

Beachten Sie die Rekursion – dies ist eine der elegantesten fortgeschrittenen Techniken in TypeScript, die es ermöglicht, unendlich verschachtelte Strukturen zu verarbeiten.

\n\n

3. Mapped types: Transformation von Objekten

\n\n

Mapped types ermöglichen es, neue Typen auf der Grundlage vorhandener Typen zu erstellen, indem deren Schlüssel durchlaufen werden. Dies ist ein unverzichtbares Werkzeug zur Erstellung generischer Hilfsfunktionen:

\n\n
type Readonly<T> = {\n  readonly [K in keyof T]: T[K];\n};\n\ntype Partial<T> = {\n  [K in keyof T]?: T[K];\n};\n
\n\n

Aber wir können noch weiter gehen. Erstellen wir einen Typ, der alle Eigenschaften optional macht, jedoch nur, wenn es sich um Strings handelt:

\n\n
type PartialStringProps<T> = {\n  [K in keyof T]: T[K] extends string ? T[K] | undefined : T[K];\n};\n\ninterface Config {\n  nam                

Blogs

Buchempfehlungen