Types de tuples dactylographiés

Comment taper au mieux les tuples dans Typescript 4.2 et versions ultérieures

Un examen plus approfondi des tuples Typescript

Pour rappel, un tuple dans sa définition la plus basique n'est qu'une structure de données composée de plusieurs parties. Dans le cadre de l'utilisation de tuples dans les langages de programmation, tels que Typescript, il est également important de noter que les données sont le plus souvent ordonnées.

Un exemple simple montre comment les tuples sont définis dans Typescript.

// 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"];

Tuples avec éléments facultatifs

Au fur et à mesure que Typescript s'améliorait au fil du temps, son implémentation des tuples s'est également améliorée. Vous n'avez pas seulement à définir des éléments qui doivent être obligatoires. Vous pouvez désormais également saisir des éléments comme facultatif. Si vous ne le savez pas, Typescript utilise le point d'interrogation comme symbole général pour définir les éléments comme facultatifs, ce qui signifie qu'ils peuvent être disponibles au moment de l'exécution, mais pas obligatoirement.

Un autre exemple montre ce que je veux dire par là.

// 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"];

Éléments de repos dans les tuples Typescript

Avec les éléments de repos, vous disposez d'un type très puissant qui marque tous les éléments suivants dans le tuple d'un type donné. Supposons donc que vous ayez un tuple avec deux éléments et le second défini comme un élément de repos, vous pouvez alors fournir 2 + n éléments au moment de l'exécution à cette variable de tuple.

Pour affiner la définition, jusqu'à récemment, un tel élément n'était autorisé qu'à la fin d'un tuple. Cela a du sens, car cela vous permet de fournir n'importe quel nombre d'éléments au moment de l'exécution du type rest, mais rendrait les choses très compliquées pour faire la distinction entre l'élément rest et un autre élément typé.

// 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"];

Élément de tête ou de repos intermédiaire dans les types de tuple

En faisant progresser l'élément rest pour les tuples, vous pouvez créer des implémentations encore plus sophistiquées depuis la sortie de Typescript 4.2. Et je dois m'excuser ici : juste quelques phrases plus tôt, j'ai écrit qu'il est obligatoire d'utiliser un élément de repos uniquement comme dernier. Cette restriction n'est plus vraie depuis Typescript 4.2, car vous pouvez désormais placer des éléments de repos presque n'importe où dans un tuple.

Mais avec seulement quelques restrictions, Typescript fournit désormais une syntaxe très agréable pour les tuples avancés. Les éléments de repos peuvent apparaître n'importe où dans un tuple tant qu'ils sont conformes aux deux règles suivantes

  • il n'est pas suivi d'un élément facultatif
  • aucun autre élément de repos ne suit le premier

Avant de parler trop de théorie, voyons un exemple.

// 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.

Le reste de cet article

Pour conclure ce guide compact sur les tuples dans Typescript, nous avons examiné l'implémentation de base, puis avons examiné quelques exemples plus avancés pour voir comment Typescript permet un système de type très flexible en ce qui concerne les tuples. J'espère que vous avez apprécié l'article et si vous êtes curieux d'en savoir plus, consultez les messages suggérés ci-dessous.

Suggestions

Connexe

Addenda

Langues