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/*"] } } } |
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/*"] } } } |
| 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)); } |
| Code TypeScript : | Sélectionner tout |
declare function stringifyAll(...elements: unknown[]): Array<string>;
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 |
Si seulement il y avait un moyen de mapper uniformément chacun des types ici, pas vrai ?
Eh bien, TypeScript a déjà quelque chose qui s’y apparente. TypeScript a un concept appelé type d'objet mappé qui peut générer de nouveaux types à partir de ceux existants. Par exemple, en fonction du type de personne suivant,
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 | interface Person { name: string; age: number; isHappy: boolean; } |
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | interface StringyPerson { name: string; age: string; isHappy: string; } function stringifyPerson(p: Person) { const result = {} as StringyPerson; for (const prop in p) { result[prop] = String(p[prop]); } return result; } |
| Code TypeScript : | Sélectionner tout |
1 2 3 | type Stringify<T> = { [K in keyof T]: string }; |
Il est maintenant temps de réécrire notre fonction pour l'utiliser:
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | function stringifyProps<T>(p: T) { const result = {} as Stringify<T>; for (const prop in p) { result[prop] = String(p[prop]); } return result; } stringifyProps({ hello: 100, world: true }); // has type `{ hello: string, world: string }` |
| Code TypeScript : | Sélectionner tout |
declare function stringifyAll<T extends unknown[]>(...elements: T): Stringify<T>;
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 | let stringyCoordinates = stringifyAll(100, true); // No errors! let first: string = stringyCoordinates[0]; let second: string = stringyCoordinates[1]; |
| Code TypeScript : | Sélectionner tout |
1 2 3 | let len: 2 = stringyCoordinates.length // ~~~ // Type 'string' is not assignable to type '2'. |
| Code TypeScript : | Sélectionner tout |
1 2 3 | stringyCoordinates.forEach(x => console.log(x)); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures. |
Bien que son comportement soit cohérent sur le plan technique, la majorité de l’équipe de TypeScript a estimé que ce cas d'utilisation devrait fonctionner. Plutôt que d'introduire un nouveau concept de mappage sur un tuple, les types d'objet mappés ne font désormais que « faire ce qui est bien » lors d'une itération sur des tuples et des tableaux. Cela signifie que si vous utilisez déjà des types mappés existants tels que Partial ou Required de lib.d.ts, ils fonctionnent automatiquement sur les tuples et les tableaux.
Propriétés sur les déclarations de fonction
En JavaScript, les fonctions ne sont que des objets. Cela signifie que nous pouvons y appliquer des propriétés à notre guise :
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 | export function readFile(path) { // ... } readFile.async = function (path, callback) { // ... } |
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | export function readFile() { // ... } export namespace readFile { export function async() { // ... } } |
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 | export const FooComponent => ({ name }) => ( <div>Hello! I am {name}</div> ); FooComponent.defaultProps = { name: "(anonymous)", }; |
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | export const FooComponent => ({ name }) => ( <div>Hello! I am {name}</div> ); // Doesn't work! namespace FooComponent { export const defaultProps = { name: "(anonymous)", }; } |
Compte tenu de tout cela, Microsoft a estimé qu'il serait préférable de rendre TypeScript un peu plus « intelligent » sur ces types de modèles. Dans TypeScript 3.1, pour toute déclaration de fonction ou déclaration de const initialisée avec une fonction, le vérificateur de type analyse la portée contenant pour suivre toutes les propriétés ajoutées. Cela signifie que les deux exemples - à la fois le readFile et les exemples FooComponent - fonctionnent sans modification dans TypeScript 3.1 !
En prime, cette fonctionnalité, associée à la prise en charge de JSX.LibraryManagedAttributes par TypeScript 3.0, facilite considérablement la migration d’une base de code React non typée vers TypeScript, car elle comprend quels attributs sont facultatifs en présence de defaultProps:
| Code TypeScript : | Sélectionner tout |
1 2 3 | // TypeScript understands that both are valid: <FooComponent /> <FooComponent name="Nathan" /> |
Voir aussi :
Google s'oriente vers TypeScript et voici pourquoi, selon Evan Martin, un ingénieur de la firme qui travaille sur le langage
Babel : la version 7.0 du transpileur JavaScript est disponible avec le support de TypeScript et bien d'autres nouvelles fonctionnalités
La Release Candidate de TypeScript 3.0 vient de sortir : tour d'horizon des nouveautés de cette version majeure du surensemble typé de JavaScript
TypeScript 2.9 est disponible et intègre plusieurs nouveautés, le langage continue de compléter son système de typage
Vous avez lu gratuitement 3 180 articles depuis plus d'un an.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.