Redirection de version pour TypeScript via typesVersionsDe nombreux utilisateurs de TypeScript et de JavaScript aiment utiliser les fonctionnalités de pointe des langages et des outils. Cela peut parfois créer une situation difficile où les responsables sont obligés de choisir entre prendre en charge de nouvelles fonctionnalités TypeScript et ne pas avoir de cassure avec les anciennes versions de TypeScript.
Par exemple, si vous maintenez une bibliothèque qui utilise le type unknown de TypeScript 3.0, tous vos utilisateurs utilisant des versions antérieures seront pénalisés. Jusqu’à présent, il n’existait malheureusement pas de moyen de fournir des types pour les versions antérieures à 3.0 de TypeScript tout en fournissant également des types pour les versions 3.0 et ultérieures.
Désormais, quand vous utilisez la résolution du module Node dans TypeScript 3.1, lorsque TypeScript ouvre un fichier package.json pour déterminer quels fichiers doivent être lus, il examine d'abord un nouveau champ appelé typesVersions. Un package.json avec un champ typesVersions peut ressembler à ceci:
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 | { "name": "package-name", "version": "1.0", "types": "./index.d.ts", "typesVersions": { ">=3.1": { "*": ["ts3.1/*"] } } } |
Ce package.json demande à TypeScript de vérifier si la version actuelle de TypeScript est en cours d'exécution. S'il s'agit d'une version 3.1 ou ultérieure, il identifie le chemin par lequel le package a été importé et lit à partir du dossier ts3.1 du package. C’est ce que {"*": ["ts3.1 / *"]} signifie - si vous êtes familier avec le mappage des chemins, cela fonctionne exactement comme ça.
Donc, dans l'exemple ci-dessus, si nous importons depuis "package-name", nous essaierons de résoudre [...]/node_modules/package-name/ts3.1/index.d.ts (et d'autres chemins d'accès) lors de l'exécution en TypeScript 3.1. Si nous importons depuis package-name / foo, nous essaierons de rechercher [...]/node_modules/package-name/ts3.1/foo.d.ts et [...]/node_modules/package-name/ts3.1/foo/index.d.ts.
Que se passe-t-il si nous n'exécutons pas TypeScript 3.1 dans cet exemple ? Eh bien, si aucun des champs de typesVersions n'est mis en correspondance, TypeScript revient au champ types, donc ici TypeScript 3.0 et les versions antérieures seront redirigés vers [...]/node_modules/package-name/index.d.ts.
Multiple champs
typesVersions peut prendre en charge plusieurs champs où chaque nom de champ est spécifié par la plage à laquelle correspondre.
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | { "name": "package-name", "version": "1.0", "types": "./index.d.ts", "typesVersions": { ">=3.2": { "*": ["ts3.2/*"] }, ">=3.1": { "*": ["ts3.1/*"] } } } |
Les plages pouvant se chevaucher, la détermination de la redirection s’applique à l’ordre. Cela signifie que dans l'exemple ci-dessus, même si les appariements > = 3.2 et > = 3.1 prennent en charge TypeScript 3.2 et versions ultérieures, l'inversion de l'ordre peut avoir un comportement totalement différent.
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | { "name": "package-name", "version": "1.0", "types": "./index.d.ts", "typesVersions": { // NOTE: this won't work! ">=3.1": { "*": ["ts3.1/*"] }, ">=3.2": { "*": ["ts3.2/*"] } } } |
Types de tableau et de tuple mappables
Mapper des valeurs dans une liste est l’un des schémas les plus courants de la programmation. À titre d’exemple, regardons le code JavaScript suivant :
| Code TypeScript : | Sélectionner tout |
1 2 3 | function stringifyAll(...elements) { return elements.map(x => String(x)); } |
La fonction stringifyAll prend un nombre quelconque de valeurs, convertit chaque élément en chaîne, place chaque résultat dans un nouveau tableau et renvoie ce tableau. Si nous voulons avoir le type le plus général pour stringifyAll, nous le déclarerons comme suit:
| Code TypeScript : | Sélectionner tout |
declare function stringifyAll(...elements: unknown[]): Array<string>;
Dans son essence, cette portion de code déclare : « cette chose prend un nombre quelconque d'éléments, et retourne un tableau de chaînes ». Cependant, nous avons perdu un peu d’informations sur les éléments de cette transformation.
Plus précisément, le système de types ne se souvient pas du nombre d’éléments transmis à l’utilisateur, de sorte que notre type de sortie n’a pas non plus de longueur connue. Nous pouvons faire quelque chose comme ça avec des surcharges:
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 | declare function stringifyAll(...elements: []): string[]; declare function stringifyAll(...elements: [unknown]): [string]; declare function stringifyAll(...elements: [unknown, unknown]): [string, string]; declare function stringifyAll(...elements: [unknown, unknown, unknown]): [string, string, string]; // ... etc |
Plutôt lourd n’est-ce pas ? Et nous n'avons même pas encore couvert quatre éléments. Vous finissez par créer des cas particuliers de toutes les surcharges possibles et vous vous retrouvez avec ce que Microsoft appelle le problème de la « mort par milliers de surcharges ». Bien sûr, nous pourrions utiliser des types conditionnels au lieu de surcharges, mais vous auriez alors...
La fin de cet article est réservée aux abonnés. Soutenez le Club Developpez.com en prenant un abonnement pour que nous puissions continuer à vous proposer des publications.