« Cette version du langage représente notre prochaine génération de versions de TypeScript, alors que nous approfondissons l'expressivité, la productivité et l'évolutivité.
« Si vous n'êtes pas familier avec TypeScript, c'est un langage qui s'appuie sur JavaScript en ajoutant une syntaxe pour les types statiques. L'idée est qu'en écrivant les types de vos valeurs et où elles sont utilisées, vous pouvez utiliser TypeScript pour vérifier le type de votre code et vous informer des erreurs avant d'exécuter votre code (et même avant d'enregistrer votre fichier). Vous pouvez ensuite utiliser le compilateur TypeScript pour supprimer les types de votre code et vous laisser avec un JavaScript propre et lisible qui s'exécute n'importe où. Au-delà de la vérification, TypeScript utilise également des types statiques pour alimenter d'excellents outils d'édition tels que l'autocomplétion, la navigation dans le code, les refactorisations, etc. En fait, si vous avez utilisé JavaScript dans un éditeur tel que Visual Studio Code ou Visual Studio, vous avez déjà utilisé une expérience optimisée par types et TypeScript.
« Avec TypeScript 4.0, il n'y a pas de changements majeurs de rupture. En fait, si vous êtes nouveau dans le langage, c'est le meilleur moment pour commencer à l'utiliser. La communauté est déjà là et grandit, avec un code fonctionnel et de nouvelles ressources à apprendre. Et une chose à garder à l'esprit: malgré toutes les bonnes choses que nous apportons dans la version 4.0, il vous suffit de connaître les bases de TypeScript pour être productif! »
De TypeScript 3 à TypeScript 4
TypeScript fait aujourd'hui partie intégrante de la pile JavaScript de nombreuses personnes. Sur npm, TypeScript a enregistré plus de 50 millions de téléchargements mensuels pour la première fois en juillet!
À partir de la version 3.0, il y a un grand nombre de changements. L'unification des types de tuples et des listes de paramètres a été parmi les éléments mis en exergue. La version comprenait également des références de projet pour aider à augmenter, organiser et effectuer des partages entre les bases de code. Un petit changement qui a eu un grand impact a été que la 3.0 a introduit une alternative de type sûr à any appelé unknown.
TypeScript 3.1 a étendu les capacités des types mappés pour fonctionner sur les types de tuple et de tableau, et a considérablement simplifié l'attachement de propriétés aux fonctions sans recourir à des fonctionnalités d'exécution spécifiques à TypeScript qui ne sont plus utilisées.
TypeScript 3.2 permettait la diffusion d'objets sur des types génériques et exploitait les capacités de la 3.0 pour mieux modéliser la métaprogrammation avec des fonctions en tapant strictement bind, call et apply. TypeScript 3.3 s'est un peu concentré sur la stabilité après 3.2, mais a également apporté des améliorations lors de l'utilisation de méthodes de type union, et a ajouté des builds incrémentiels de fichier en mode --build.
Dans la version 3.4, Microsoft s'est penché davantage sur la prise en charge des modèles fonctionnels, avec une meilleure prise en charge des structures de données immuables et une meilleure inférence sur les fonctions génériques d'ordre supérieur. Comme bonus, cette version a introduit l'indicateur --incremental, un moyen d'obtenir des compilations et des vérifications de type plus rapides en évitant une reconstruction complète à chaque exécution de TypeScript, sans références de projet.
Avec TypeScript 3.5 et 3.6, nous avons assisté à un resserrement des règles du système de types, ainsi qu'à des règles de vérification de compatibilité plus intelligentes.
TypeScript 3.7 était une version très remarquable, car elle présentait une riche combinaison de nouvelles fonctionnalités de système de type avec des fonctionnalités ECMAScript. Du côté du système de type, nous avons vu des références d'alias de type récursives et la prise en charge des fonctions de style assertion, deux caractéristiques uniques du système de type. Du côté de JavaScript, la version a apporté le chaînage et la fusion facultatifs, deux des fonctionnalités les plus demandées par les utilisateurs de TypeScript et de JavaScript.
Beaucoup plus récemment, TypeScript 3.8 et 3.9 ont apporté des importations / exportations de type uniquement, ainsi que des fonctionnalités ECMAScript telles que les champs privés, await dans les modules et de nouvelles syntaxes export *. Ces versions ont également permis d'optimiser les performances et l'évolutivité.
Passons en revue quelques nouvelles fonctionnalités de TypeScript 4.0.
Types de tuple variadique (qui accepte un nombre variable de paramètres)
Considérez une fonction en JavaScript appelée concat, qui prend deux types de tableau ou de tuple et les concatène ensemble comme un nouveau tableau.
Code JavaScript : | Sélectionner tout |
1 2 3 | function concat(arr1, arr2) { return [...arr1, ...arr2]; } |
Considérez également tail, qui prend un tableau ou un tuple, et renvoie tous les éléments sauf le premier.
Code JavaScript : | Sélectionner tout |
1 2 3 4 | function tail(arg) { const [_, ...result] = arg; return result } |
Comment pourrions-nous taper l'un de ces éléments dans TypeScript?
Pour concat, la seule chose valable que nous pouvions faire dans les anciennes versions du langage était d'essayer d'écrire des surcharges. Une fonction surchargée est une fonction qui est définie plusieurs fois dans une application afin d’offrir différentes logiques à l’application.
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 | function concat<>(arr1: [], arr2: []): [A]; function concat<A>(arr1: [A], arr2: []): [A]; function concat<A, B>(arr1: [A, B], arr2: []): [A, B]; function concat<A, B, C>(arr1: [A, B, C], arr2: []): [A, B, C]; function concat<A, B, C, D>(arr1: [A, B, C, D], arr2: []): [A, B, C, D]; function concat<A, B, C, D, E>(arr1: [A, B, C, D, E], arr2: []): [A, B, C, D, E]; function concat<A, B, C, D, E, F>(arr1: [A, B, C, D, E, F], arr2: []): [A, B, C, D, E, F];) |
Ce qui nous donne donc sept surcharges lorsque le deuxième tableau est toujours vide. Ajoutons-en pour quand arr2 a un argument.
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 | function concat<A2>(arr1: [], arr2: [A2]): [A2]; function concat<A1, A2>(arr1: [A1], arr2: [A2]): [A1, A2]; function concat<A1, B1, A2>(arr1: [A1, B1], arr2: [A2]): [A1, B1, A2]; function concat<A1, B1, C1, A2>(arr1: [A1, B1, C1], arr2: [A2]): [A1, B1, C1, A2]; function concat<A1, B1, C1, D1, A2>(arr1: [A1, B1, C1, D1], arr2: [A2]): [A1, B1, C1, D1, A2]; function concat<A1, B1, C1, D1, E1, A2>(arr1: [A1, B1, C1, D1, E1], arr2: [A2]): [A1, B1, C1, D1, E1, A2]; function concat<A1, B1, C1, D1, E1, F1, A2>(arr1: [A1, B1, C1, D1, E1, F1], arr2: [A2]): [A1, B1, C1, D1, E1, F1, A2]; |
Il est donc clair que cela devient déraisonnable. Malheureusement, vous vous retrouveriez également avec les mêmes types de problèmes lors de la saisie d'une fonction comme tail.
C'est un autre cas de ce que l'équipe TypeScript aime appeler « la mort par mille surcharges », et cela ne résout même pas le problème en général. Il ne donne que les types corrects pour autant de surcharges que nous voulons écrire. Si nous voulions créer un cas fourre-tout, nous aurions besoin d'une surcharge comme celle-ci:
Code TypeScript : | Sélectionner tout |
function concat<T, U>(arr1: T[], arr2, U[]): Array<T | U>;
Mais cette signature n'encode rien à propos de la longueur de l'entrée ou l'ordre des éléments lors de l'utilisation de tuples.
TypeScript 4.0 apporte deux changements fondamentaux, ainsi que des améliorations d'inférence, pour rendre leur saisie possible.
Le premier changement est que les spreads (Spread ou REST paramètre est une technique qui peut être utilisée dans le cas où nous ne connaissons pas le nombre d'arguments qui sera envoyé à la fonction, un paramètre spread est dénoté par trois points (...) qui se placent devant le nom de paramètre) dans la syntaxe de type tuple peuvent désormais être génériques. Cela signifie que nous pouvons représenter des opérations d'ordre supérieur sur des tuples et des tableaux même lorsque nous ne connaissons pas les types réels sur lesquels nous opérons. Lorsque des spreads génériques sont instanciés (ou remplacés par un type réel) dans ces types de tuples, ils peuvent produire d'autres ensembles de types de tableau et de tuple.
Par exemple, cela signifie que nous pouvons taper une fonction comme queue, sans notre problème de «mort par mille surcharges».
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | function tail<T extends any[]>(arr: readonly [any, ...T]) { const [_ignored, ...rest] = arr; return rest; } const myTuple = [1, 2, 3, 4] as const; const myArray = ["hello", "world"]; // type [2, 3, 4] const r1 = tail(myTuple); // type [2, 3, ...string[]] const r2 = tail([...myTuple, ...myArray] as const); |
Le deuxième changement est que les éléments spread peuvent apparaître n'importe où dans un tuple - pas seulement à la fin!
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 | type Strings = [string, string]; type Numbers = [number, number]; // [string, string, number, number] type StrStrNumNum = [...Strings, ...Numbers]; |
Auparavant, TypeScript générait une erreur comme celle-ci.
Envoyé par TypeScript
Lorsque nous répartissons un type sans longueur connue, le type résultant devient également illimité et tous les éléments consécutifs sont pris en compte dans le type d'élément de repos résultant.
Code : | Sélectionner tout |
1 2 3 4 5 | type Strings = [string, string]; type Numbers = number[] // [string, string, ...Array<number | boolean>] type Unbounded = [...Strings, ...Numbers, boolean]; |
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 | type Arr = readonly any[]; function concat<T extends Arr, U extends Arr>(arr1: T, arr2: U): [...T, ...U] { return [...arr1, ...arr2]; } |
Bien que cette signature soit encore un peu longue, il ne s'agit toujours que d'une signature, elle ne doit être écrite qu'une seule fois, et elle donne en fait un comportement prévisible sur tous les tableaux et tuples.
Si l'équipe est plutôt satisfaite de cette fonctionnalité, elle reconnaît qu'il existe également d'autres scénarios plus sophistiqués. Par exemple, considérons une fonction pour appliquer partiellement des arguments appelés partialCall.partialCall qui prend une fonction avec les quelques arguments initiaux attendus par cette fonction. Elle renvoie ensuite une nouvelle fonction qui prend tous les autres arguments dont la fonction a besoin et les appelle ensemble.
Code TypeScript : | Sélectionner tout |
1 2 3 | function partialCall(f, ...headArgs) { return (...tailArgs) => f(...headArgs, ...tailArgs) } |
TypeScript 4.0 améliore le processus d'inférence pour les paramètres de repos et les éléments de tuple de repos.
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 | type Arr = readonly unknown[]; function partialCall<T extends Arr, U extends Arr, R>(f: (...args: [...T, ...U]) => R, ...headArgs: T) { return (...b: U) => f(...headArgs, ...b) } |
Dans ce cas, partialCall comprend les paramètres qu'il peut et ne peut pas prendre initialement et renvoie des fonctions qui acceptent et rejettent de manière appropriée tout ce qui reste.
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | const foo = (x: string, y: number, z: boolean) => {} // This doesn't work because we're feeding in the wrong type for 'x'. const f1 = partialCall(foo, 100); // ~~~ // error! Argument of type 'number' is not assignable to parameter of type 'string'. // This doesn't work because we're passing in too many arguments. const f2 = partialCall(foo, "hello", 100, true, "oops") // ~~~~~~ // error! Expected 4 arguments, but got 5. // This works! It has the type '(y: number, z: boolean) => void' const f3 = partialCall(foo, "hello"); // What can we do with f3 now? f3(123, true); // works! f3(); // error! Expected 2 arguments, but got 0. f3(123, "hello"); // ~~~~~~~ // error! Argument of type '"hello"' is not assignable to parameter of type 'boolean'. |
Source : billet TypeScript