Programmiersprache: TypeScript 4.1 erweitert Mapped und String Literal Types

String Literal Types dürfen nun Templates enthalten, und Entwickler können die Schlüssel für Mapped Types anpassen.

Lesezeit: 2 Min.
In Pocket speichern
vorlesen Druckansicht Kommentare lesen

(Bild: , gemeinfrei)

Von
  • Rainald Menge-Sonnentag

Microsoft hat die erste Betaversion von TypeScript 4.1 veröffentlicht. Das Release bringt erweiterte Flexibilität für String Literal Types und Mapped Types mit. Außerdem gibt es ein neues Flag, das Fehler im Umgang mit Index Signatures verhindern soll, und das JavaScript-Superset erlaubt neuerdings rekursive Conditional Types.

Die neuen Template Literal Types erweitern String Literal Types in derselben Art und Weise, wie Template Literals in JavaScript Strings erweitern: Die einzelnen String-Literale dürfen Vorlagen mit der Syntax ${expression} enthalten. In String Literals Types dient eine Reihe von Strings zum Definieren der erlaubten Werte.

Nützlich ist der Einsatz unter anderem beim Verknüpfen von String Literal Types wie in folgendem, um einen Donut-Bezug gekürzten Beispiel aus dem TypeScript-Blog:

type VerticalAlignment = "top" | "middle" | "bottom";
type HorizontalAlignment = "left" | "center" | "right";

// Takes
//   | "top-left"    | "top-center"    | "top-right"
//   | "middle-left" | "middle-center" | "middle-right"
//   | "bottom-left" | "bottom-center" | "bottom-right"
declare function 
  setAlignment(value: 
             `${VerticalAlignment}-${HorizontalAlignment}`): void;

setAlignment("top-left");   // works!
setAlignment("top-middel"); // error!
setAlignment("top-pot");    // error! 

Der Code kombiniert die beiden String Literal Types VerticalAlignment und HorizontalAlignment und bietet damit beliebige Kombinationen der vertikalen und horizontalen Ausrichtung, also insgesamt neun definierte Werte aus drei mal drei Vorgaben. Im Blog finden sich neben dem einfachen deutlich komplexere Beispiele, die auch das Erschließen des jeweiligen Typs behandeln.

Mapped Types erlauben das Erstellen von Objekttypen anhand von Schlüsseln wie in type Beispiel = {[K in "a" | "b" | "c"]?: boolean}; oder als Erweiterung anderer Typen wie in type Partial<T> = {[K in keyof T]?: T[K]};.

Mit TypeScript 4.1 ist es möglich, die Keys manuell über das Schlüsselwort as zu bestimmen. In Kombination mit den Template Literal Types lasen sich neue Property-Namen auf Basis vorhandener Namen erzeugen wie in folgendem Beitrag aus dem TypeScript-Blog zum Erstellen von Getter-Wrappern für die Variablen eines Interfaces:

type Getters<T> = {
  [K in keyof T as `get${capitalize K}`]: () => T[K]
};

interface Person {
  name: string;
  age: number;
  location: string;
}

type LazyPerson = Getters<Person>;

Die mit dem aktuellen Release eingeführten Recursive Conditional Types erlauben einen flexibleren Umgang mit konditionalen Typen. Konkret dürfen diese neuerdings sich selbst referenzieren. Der Blogbeitrag führt dazu ein Beispiel zum Ermitteln der Typen in verschachtelten Arrays auf:

type ElementType<T> =
  T extends ReadonlyArray<infer U> ? ElementType<U> : T;

function deepFlatten<T extends readonly unknown[]>(x: T): 
  ElementType<T>[] {
    throw "not implemented";
}

// All of these return the type 'number[]':
deepFlatten([1, 2, 3]);
deepFlatten([[1], [2, 3]]);
deepFlatten([[1], [[2]], [[[3]]]]);

Allerdings weist der Blogbeitrag explizit darauf hin, dass durch rekursive Typen die für das Type-Checking benötigte Zeit deutlich wachsen kann und Entwickler Recursive Conditional Types verantwortungsvoll und sparsam verwenden sollten.

Nennenswert ist zudem der neu eingeführte TypeScript-Parameter --noUncheckedIndexedAccess für den Zugriff auf Index Signatures:

interface Options {
  path: string;
  permissions: number;

  // Extra properties are caught by this index signature.
  [propName: string]: string | number;
}

Bisher ging TypeScript bei obigem Konstrukt davon aus, dass alle Properties entweder vom Typ string oder number sein müssen. Mit dem neuen Flag berücksichtigt der Compiler, dass der direkte Zugriffe auf solche Properties wie in foo.bar oder über den Index wie in foo["bar"] potenziell undefined sein können.

Weitere Neuerungen wie der Einsatz von paths ohne baseUrl lassen sich dem TypeScript-Blog entnehmen. Wer die Beta testen möchte, kann sie von NuGet herunterladen oder über npm mit dem Befehl npm install typescript installieren. Der Release Candidate ist für Oktober angekündigt und erfahrungsgemäß dürfte wie bei TypeScript 4.0 die endgültige Variante kurz darauf zu erwarten sein.

(rme)