Kodokon kodokon.com

Template-Literal-Typen: zur Compile-Zeit berechnete Zeichenketten

Nutze Template-Literal-Typen, um Unions über kartesische Produkte zu erzeugen und String-Literale mit infer zu zerlegen.

9 Min. · 3 Fragen

Diese Lektion in Kodokon öffnen

Template-Literal-Typen bringen die Syntax der Template-Literale in die Welt der Typen. Interpoliere eine Union, und der Compiler berechnet das kartesische Produkt: Jede mögliche Kombination wird zu einem Mitglied des Ergebnisses. Das ist das Werkzeug, mit dem du Event-Schlüssel, Routen oder Design-System-Tokens zeichengenau typisierst.

TYPESCRIPT
type Lang = "fr" | "en";
type Theme = "light" | "dark";

type ThemeKey = `${Lang}-${Theme}`;
// "fr-light" | "fr-dark" | "en-light" | "en-dark"
Zwei interpolierte Unions: ein kartesisches Produkt aus 4 Mitgliedern.

Die wahre Stärke zeigt sich in Kombination mit infer: Ein Template-Literal-Typ wird zu einem Abgleichmuster für Zeichenketten. Der Compiler zerschneidet das Eingabeliteral, um jedes Segment zu treffen; wenn mehrere Aufteilungen möglich sind, gewinnt das erste Vorkommen des Trennzeichens. Ergänze Rekursion, und du erhältst echte Parser, die zur Compile-Zeit ausgewertet werden.

TYPESCRIPT
type Split<S extends string, Sep extends string> =
  S extends `${infer Head}${Sep}${infer Rest}`
    ? [Head, ...Split<Rest, Sep>]
    : [S];

type Parts = Split<"a.b.c", ".">;
// ["a", "b", "c"]
Ein vollständig statisches, rekursives Aufteilen.

Dieses Muster glänzt beim Extrahieren der Parameter einer Route im Express-Stil. Jeder rekursive Aufruf verbraucht ein :param-Segment, und die finale Union listet jeden Parameternamen auf, den du anschließend in ein Record geben kannst, um die Signatur des Handlers zu typisieren.

TYPESCRIPT
type Params<Path extends string> =
  Path extends `${string}:${infer P}/${infer Rest}`
    ? P | Params<Rest>
    : Path extends `${string}:${infer P}`
      ? P
      : never;

type RouteParams = Params<"/users/:id/posts/:postId">;
// "id" | "postId"
Routenparameter zur Compile-Zeit extrahieren.

TypeScript liefert vier intrinsische Hilfstypen mit - Uppercase, Lowercase, Capitalize, Uncapitalize -, die direkt im Compiler implementiert sind (das Schlüsselwort intrinsic), du kannst sie also nicht selbst nachbauen. Ein weiteres Muster aus der Produktion: string & {}. In einer Union würde string die Literale verschlucken und der Editor würde die Autovervollständigung verlieren; die Intersection mit {} erzeugt einen gleichwertigen, aber eigenständigen Typ, den der Compiler nicht reduziert.

TYPESCRIPT
type Method = "get" | "post";
type Handler = `on${Capitalize<Method>}`;
// "onGet" | "onPost"

type KnownColor = "red" | "green" | "blue";
type Color = KnownColor | (string & {});

declare function paint(color: Color): void;

paint("red");     // suggested by autocompletion
paint("#ff8800"); // also accepted
Intrinsische Typen und der Trick string & {} für die Autovervollständigung.

Wissenscheck

Stelle sicher, dass du die wichtigsten Punkte dieser Lektion behalten hast.

  1. Wenn A 3 Mitglieder hat und B 4, wie viele Mitglieder enthält dann ${A}-${B}?
    • 7
    • 12
    • 1
    • 4
  2. Was erzeugt Capitalize, angewendet auf das Literal "hello world"?
    • "Hello World"
    • "Hello world"
    • "HELLO WORLD"
  3. Warum schreibt man KnownColor | (string & {}) statt KnownColor | string?
    • Um Zeichenketten außerhalb von KnownColor zu verbieten
    • Damit string die Literale nicht verschluckt und die Autovervollständigung erhalten bleibt
    • Um die Performance des Compilers zu verbessern
    • Die beiden Formen sind strikt gleichwertig