Ein genauerer Blick auf Typoskript-Tupel
Zur Erinnerung: Ein Tupel ist in seiner einfachsten Definition nur eine Datenstruktur, die aus mehreren Teilen besteht. Bei der Verwendung von Tupeln in Programmiersprachen wie Typescript ist es auch wichtig zu beachten, dass die Daten am häufigsten geordnet sind.
Ein einfaches Beispiel zeigt, wie Tupel in Typescript definiert werden.
// This tuple is defined as a set
// of two numbers.
const scores: [number, number] = [1, 2];
// For comparison, this tuple consists
// of three elements, each of a different
// type. Not that the ordered nature of
// tuples in TS becomes very clear here.
const result: [string, number, boolean] = ["id", 101, false];
// And as a "nice-to-know", you can even
// provide lables for the tuple elements.
// This doesn't have any effect on the typesystem
// itself and only (may) improve documentation.
const output: [id: number, name: string] = [101, "Tom"];
Tupel mit optionalen Elementen
Da Typescript im Laufe der Zeit besser wurde, wurde auch die Implementierung von Tupeln. Sie müssen nicht nur Elemente definieren, die obligatorisch sein müssen. Sie können Elemente jetzt auch optional eingeben. Wenn Sie es nicht wissen, verwendet Typescript das Fragezeichen als allgemeines Symbol, um Elemente als optional zu definieren, was bedeutet, dass sie zur Laufzeit verfügbar sein können, aber nicht müssen.
Ein anderes Beispiel zeigt, was ich damit meine.
// Similar to our previous example, but in this
// case the the tuple's last element doesn't have
// to be provided (or can be undefined at runtime).
type Tuple = [id: number, name?: string];
const a: Tuple = [101];
const b: Tuple = [42, "Tom"];
Rest-Elemente in Typoskript-Tupeln
Mit Rest-Elementen haben Sie einen sehr mächtigen Typ zur Hand, der alle folgenden Elemente im Tupel eines bestimmten Typs markiert. Angenommen, Sie haben ein Tupel mit zwei Elementen und das zweite ist als Restelement definiert. Sie können dieser Tupelvariablen dann zur Laufzeit 2 + n Elemente bereitstellen.
Um die Definition einzugrenzen, war ein solches Element bis vor kurzem nur am Ende eines Tupels erlaubt. Dies ist sinnvoll, da Sie damit zur Laufzeit beliebig viele Elemente des Typs rest bereitstellen können, die Unterscheidung zwischen dem Element rest und einem weiteren, anderen typisierten Element jedoch sehr kompliziert werden würde.
// This example might be a tuple type
// for a CLI similar to Node.js. The first
// two elements are system-internal.
//
// Starting from the 3rd element, a user can
// provide as much arguments as desired, yet
// we can still cleanly handle it with TS. Nice!
let input: [number, boolean, ...string[]];
// Just to show that we really can provide any
// number of rest elements, including 0.
e = [0, false, "max-cache", "1024", "debug", "false"];
e = [0, false];
e = [0, false, "verbose"];
Führendes oder mittleres Restelement in Tupeltypen
Wenn Sie das rest-Element für Tupel erweitern, können Sie seit der Veröffentlichung von Typescript 4.2 noch ausgefeiltere Implementierungen erstellen. Und hier muss ich mich entschuldigen: Nur ein paar Sätze zu früh habe ich geschrieben, dass es zwingend erforderlich ist, ein Restelement nur als letztes zu verwenden. Diese Einschränkung gilt seit Typescript 4.2 nicht mehr, da Sie nun Rest-Elemente fast überall in einem Tupel platzieren können.
Aber mit nur wenigen Einschränkungen bietet Typescript jetzt eine sehr schöne Syntax für fortgeschrittene Tupel. Rest-Elemente können überall innerhalb eines Tupels vorkommen, solange sie den folgenden beiden Regeln entsprechen
- es folgt kein optionales Element
- kein weiteres Restelement folgt dem ersten
Bevor wir zu viel Theorie reden, schauen wir uns ein Beispiel an.
// And here comes the fancy part: rest elements
// *not only* at the end of a tuple.
//
// Note: this example is taken directly from the
// TS documentation. For more details, check out the
// links in the addendum.
let foo: [...string[], number];
foo = [123];
foo = ["hello", 123];
foo = ["hello!", "hello!", "hello!", 123];
let bar: [boolean, ...string[], boolean];
bar = [true, false];
bar = [true, "some text", false];
bar = [true, "some", "separated", "text", false];
// And here's an example that shows how the
// type system would catch your errors:
interface Clown { /*...*/ }
interface Joker { /*...*/ }
let StealersWheel: [...Clown[], "me", ...Joker[]];
// ~~~~~~~~~~ Error!
// A rest element cannot follow another rest element.
let StringsAndMaybeBoolean: [...string[], boolean?];
// ~~~~~~~~ Error!
// An optional element cannot follow a rest element.
Der Rest dieses Artikels
Zum Abschluss dieses kompakten Leitfadens zu Tupeln in Typescript haben wir uns die grundlegende Implementierung angesehen und dann einige fortgeschrittenere Beispiele durchgesehen, um zu sehen, wie Typescript ein sehr flexibles Typsystem in Bezug auf Tupel ermöglicht. Ich hoffe, Ihnen hat der Artikel gefallen und wenn Sie neugierig sind, mehr zu erfahren, sehen Sie sich die vorgeschlagenen Beiträge weiter unten an.