TypeScript est un langage de programmation libre et open source développé par Microsoft qui a pour but d'améliorer et de sécuriser la production de code JavaScript. Il s'agit d'un surensemble du JavaScript qui apporte un typage statique et optionnel des variables. Il permet de détecter certaines erreurs en amont et se compile en JavaScript pour une utilisation côté navigateur ou côté serveur à l'aide de NodeJS. Notons qu’avec l’utilitaire npm les développeurs JavaScript peuvent partager et réutiliser facilement leur code. Il facilite la mise à jour du code et est distribué avec Node.js. TypeScript présente plusieurs avantages :
- la prévention des bogues et la maintenabilité du code ;
- la prise en charge des interfaces, des sous-interfaces, des classes, des sous-classes ;
- la capacité de programmer en orienté objet avec l’héritage des membres privés et des interfaces.
Voici, ci-dessous, les nouveautés apportées par la Release Candidate de TypeScript 4.6 :
Autoriser le code dans les constructeurs avant super()
Dans les classes JavaScript, il est obligatoire d'appeler super() avant de s'y référer. TypeScript applique également cette règle, bien qu'il soit un peu trop strict dans sa façon de l'assurer. En TypeScript, c'était auparavant une erreur de contenir du code au début d'un constructeur si sa classe contenante avait des initialisateurs de propriétés.
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Base { // ... } class Derived extends Base { someProperty = true; constructor() { // error! // have to call 'super()' first because it needs to initialize 'someProperty'. doSomeStuff(); super(); } } |
Cela permettait de vérifier à peu de frais que super() était appelé avant que ceci ne soit référencé, mais cela finit par rejeter beaucoup de code valide. TypeScript 4.6 est maintenant beaucoup plus indulgent dans cette vérification et permet à d'autres codes de s'exécuter avant super(), tout en s'assurant toujours que super() se produit au niveau supérieur avant toute référence à ceci.
Analyse du flux de contrôle pour les Unions Discriminées Destructibles
TypeScript est capable de restreindre les types sur la base de ce que l'on appelle une propriété discriminante. Par exemple, dans l'extrait de code suivant, TypeScript est capable de restreindre le type d'action en fonction de chaque fois que nous vérifions la valeur de kind.
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | type Action = | { kind: "NumberContents", payload: number } | { kind: "StringContents", payload: string }; function processAction(action: Action) { if (action.kind === "NumberContents") { // `action.payload` is a number here. let num = action.payload * 2 // ... } else if (action.kind === "StringContents") { // `action.payload` is a string here. const str = action.payload.trim(); // ... } } |
Cela permet de travailler avec des objets qui peuvent contenir différentes données, mais un champ commun indique quelles sont les données de ces objets.
C'est très courant en TypeScript ; cependant, selon certains préférences, vous auriez pu vouloir déstructurer le type et la charge utile dans l'exemple ci-dessus. Peut-être quelque chose comme ce qui suit :
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | type Action = | { kind: "NumberContents", payload: number } | { kind: "StringContents", payload: string }; function processAction(action: Action) { const { kind, payload } = action; if (kind === "NumberContents") { let num = payload * 2 // ... } else if (kind === "StringContents") { const str = payload.trim(); // ... } } |
Auparavant, TypeScript se trompait à ce sujet une fois que le type et la charge utile étaient extraits du même objet dans des variables, ils étaient considérés comme totalement indépendants. Lors de la déstructuration de propriétés individuelles dans une déclaration const, ou lors de la déstructuration d'un paramètre dans des variables qui ne sont jamais assignées, TypeScript vérifiera si le type déstructuré est une union discriminée. Si c'est le cas, TypeScript peut maintenant réduire les types de variables en fonction des vérifications d'autres variables. Ainsi, dans notre exemple, une vérification sur le type réduit le type de la charge utile.
Amélioration des vérifications de la profondeur de récursion
TypeScript a quelques défis intéressants en raison du fait qu'il est construit sur un système de type structurel qui fournit également des génériques.
Dans un système de type structurel, les types d'objets sont compatibles en fonction des membres qu'ils possèdent.
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | interface Source { prop: string; } interface Target { prop: number; } function check(source: Source, target: Target) { target = source; // error! // Type 'Source' is not assignable to type 'Target'. // Types of property 'prop' are incompatible. // Type 'string' is not assignable to type 'number'. } |
Analyse du flux de contrôle pour les Unions Discriminées Destructibles
TypeScript est capable de restreindre les types sur la base de ce que l'on appelle une propriété discriminante. Par exemple, dans l'extrait de code suivant, TypeScript est capable de restreindre le type d'action en fonction de chaque fois que nous vérifions la valeur de kind.
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 | interface Foo<T> { prop: T; } declare let x: Foo<Foo<Foo<Foo<Foo<Foo<string>>>>>>; declare let y: Foo<Foo<Foo<Foo<Foo<string>>>>>; x = y; |
Un lecteur humain peut voir que x et y devraient être incompatibles dans l'exemple ci-dessus. Bien que les types soient profondément imbriqués, c'est juste une conséquence de la façon dont ils ont été déclarés. L'heuristique avait pour but de capturer les cas où les types profondément imbriqués étaient générés par l'exploration des types, et non par un développeur qui aurait écrit ce type lui-même.
TypeScript 4.6 est maintenant capable de distinguer ces cas, et se trompe correctement sur le dernier exemple. En outre, parce que le langage n'est plus concerné par les faux-positifs des types explicitement écrits, TypeScript peut conclure qu'un type est infiniment extensible beaucoup plus tôt, et économiser un tas de travail dans la vérification de la compatibilité des types. En conséquence, les bibliothèques sur DefinitelyTyped comme redux-immutable, react-lazylog, et yup ont vu une réduction de 50 % du temps de vérification.
Améliorations de l'inférence d'accès indexé
TypeScript peut maintenant inférer correctement les types d'accès indexés qui indexent immédiatement dans un type d'objet mappé.
Code : | 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 28 | interface TypeMap { "number": number; "string": string; "boolean": boolean; } type UnionRecord<P extends keyof TypeMap> = { [K in P]: { kind: K; v: TypeMap[K]; f: (p: TypeMap[K]) => void; } }[P]; function processRecord<K extends keyof TypeMap>(record: UnionRecord<K>) { record.f(record.v); } processRecord({ kind: "string", v: "hello!", // 'val' used to implicitly have the type 'string | number | boolean', // but now is correctly inferred to just 'string'. f: val => { console.log(val.toUpperCase()); } }) |
Ce modèle était déjà supporté et permettait à TypeScript de comprendre que l'appel à record.f(record.v) est valide, mais auparavant l'appel à processRecord donnait de mauvais résultats d'inférence pour val. TypeScript 4.6 améliore cela de sorte qu'aucune assertion de type n'est nécessaire dans l'appel à processRecord.
Analyse du flux de contrôle pour les paramètres dépendants
Une signature peut être déclarée avec un paramètre de repos dont le type est une union discriminée de tuples.
Code : | Sélectionner tout |
1 2 3 | function func(...args: ["str", string] | ["num", number]) { // ... } |
Cela signifie que lorsque le premier argument est la chaîne str, alors son second argument est une chaîne, et lorsque son premier argument est la chaîne num, son second argument est un nombre. Dans les cas où TypeScript infère d'une signature avec ce type de paramètre de repos, TypeScript peut maintenant restreindre les paramètres qui dépendent des autres.
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | type Func = (...args: ["a", number] | ["b", string]) => void; const f1: Func = (kind, payload) => { if (kind === "a") { payload.toFixed(); // 'payload' narrowed to 'number' } if (kind === "b") { payload.toUpperCase(); // 'payload' narrowed to 'string' } }; f1("a", 42); f1("b", "hello"); |
--target es2022
L'option --target de TypeScript prend désormais en charge es2022. Cela signifie que des fonctionnalités comme les champs de classe ont maintenant une cible de sortie stable où elles peuvent être préservées. Cela signifie également que les nouvelles fonctionnalités intégrées comme la méthode at() sur les tableaux, Object.hasOwn, ou l'option cause sur les nouvelles erreurs peuvent être utilisées soit avec ce nouveau paramètre --target, soit avec --lib es2022.
Plus d'erreurs de syntaxe et de liaison en JavaScript
TypeScript a étendu son ensemble d'erreurs de syntaxe et de liaison dans les fichiers JavaScript.
Par exemple,s'il existe deux déclarations d'un const dans la même portée d'un fichier JavaScript, TypeScript émettra désormais une erreur sur ces déclarations.
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | const foo = 1234; // ~~~ // error: Cannot redeclare block-scoped variable 'foo'. // ... const foo = 5678; // ~~~ // error: Cannot redeclare block-scoped variable 'foo'. |
Autre exemple, TypeScript indiquera si un modificateur est utilisé de manière incorrecte.
Code : | Sélectionner tout |
1 2 3 4 5 6 7 | function container() { export function foo() { // ~~~~~~ // error: Modifiers cannot appear here. } } |
Ces erreurs peuvent être désactivées en ajoutant un // @ts-nocheck en haut du fichier.
Analyseur de traces TypeScript
Occasionnellement, les équipes peuvent rencontrer des types dont la création et la comparaison sont coûteuses en termes de calcul. TypeScript a un tag --generateTrace pour aider à identifier certains de ces types coûteux, ou parfois aider à diagnostiquer des problèmes dans le compilateur TypeScript. Alors que les informations générées par --generateTrace peuvent être utiles (en particulier avec certaines informations ajoutées dans TypeScript 4.6), elles peuvent souvent être difficiles à lire dans les visualisateurs de traces existants.
Microsoft a publié récemment un outil appelé @typescript/analyze-trace pour obtenir une vue plus digeste de ces informations.
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Thing { someProperty = 42; someMethod() { // ... } } function foo<T extends Thing>(x: T) { let { someProperty, ...rest } = x; // Used to work, is now an error! // Property 'someMethod' does not exist on type 'Omit<T, "someProperty" | "someMethod">'. rest.someMethod(); } |
la variable rest avait auparavant le type Omit parce que TypeScript analysait strictement quelles autres propriétés étaient déstructurées. Cela ne modélise pas la façon dont ...rest fonctionnerait dans une déstructuration à partir d'un type non générique parce que someMethod serait typiquement abandonné aussi. Dans TypeScript 4.6, le type de rest est Omit.
Cela peut également se produire dans les cas de déstructuration à partir de ceci. Lors de la déstructuration de this à l'aide d'un élément ...rest, les membres non diffusables et non publics sont désormais supprimés, ce qui est cohérent avec la déstructuration des instances d'une classe à d'autres endroits.
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Thing { someProperty = 42; someMethod() { // ... } someOtherMethod() { let { someProperty, ...rest } = this; // Used to work, is now an error! // Property 'someMethod' does not exist on type 'Omit<T, "someProperty" | "someMethod">'. rest.someMethod(); } } |
Les fichiers JavaScript reçoivent toujours des erreurs de grammaire et de liaison
Auparavant, TypeScript ignorait la plupart des erreurs de grammaire en JavaScript, à part l'utilisation accidentelle de la syntaxe TypeScript dans un fichier JavaScript. TypeScript montre maintenant les erreurs de syntaxe et de liaison JavaScript dans le fichier, comme l'utilisation de modificateurs incorrects, les déclarations en double, et plus encore. Ces erreurs seront généralement plus apparentes dans Visual Studio Code ou Visual Studio, mais peuvent également se produire lors de l'exécution du code JavaScript par le compilateur TypeScript.
Source : Microsoft
Et vous ?
Avez-vous une expérience avec TypeScript ?
Que pensez-vous de TypeScript ?
Quelle amélioration vous intéresse le plus sur cette version ?
Voir aussi :
Microsoft annonce TypeScript 4.3 RC, elle apporte un support de l'éditeur pour les balises @link, et améliore le temps de compilation pour les modes --incremental et --watch
Comment un développeur JavaScript anti-TypeScript est devenu un fan de TypeScript ? Voici les raisons de Chirag Swadia, un développeur JavaScript reconverti en développeur TypeScript
Microsoft publie TypeScript 4.2 avec la prise en charge de Abstract Constructor Types et des contrôles plus stricts pour l'opérateur in
.NET 6 apporterait jusqu'à 40 % de gain de vitesse par rapport .NET 5, les tests de mise en cache et de boucles serrées serraient jusqu'à 30 % plus rapide