Dans un article publié sur son blog le 25 août, Microsoft a annoncé la version 4.8 de TypeScript, extension de JavaScript qui ajoute des types statiques et la vérification de type. Elle autorise le code dans les constructeurs avant super(), améliore les vérifications de la profondeur de récursion et analyse le flux de contrôle pour les paramètres dépendants.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.
Etant donné qu’il s’agit d’un langage qui s'appuie sur JavaScript et ajoute une syntaxe pour les types, ilpermet d'intégrer les attentes et les hypothèses dans le code, et ces hypothèses peuvent ensuite être vérifiées par le vérificateur de type TypeScript. Cette vérification peut aider à éviter les fautes de frappe, l'appel de valeurs non initialisées, le mélange d'arguments pour les fonctions, etc. Les types vont au-delà de la vérification et sont utilisés pour vous offrir une expérience d'édition puissante à la fois pour TypeScript et JavaScript, en permettant la complétion de code, les définitions de type, le renommage.
Voici, ci-dessous, les nouveautés apportées par TypeScript 4.8 :
Amélioration de la réduction des intersections, de la compatibilité des unions et de l'étroitesse du champ d'application
TypeScript 4.8 apporte une série d'améliorations de correction et de cohérence sous --strictNullChecks. Ces changements affectent la façon dont les types d'intersection et d'union fonctionnent, et sont exploités dans la façon dont TypeScript réduit les types. Par exemple, unknown est proche dans l'esprit du type union {} | null | undefined car il accepte null, undefined, et tout autre type. TypeScript reconnaît maintenant cela, et permet les affectations de unknown à {} | null | undefined.
| Code : | Sélectionner tout |
1 2 3 4 | function f(x: unknown, y: {} | null | undefined) {
x = y; // always worked
y = x; // used to error, now works
} |
Un autre changement est que l'intersection de {} avec tout autre type d'objet est simplifiée jusqu'à ce type d'objet. Cela signifie que nous avons pu réécrire NonNullable en utilisant simplement une intersection avec {}, car {} & null et {} & undefined sont simplement rejetés.
| Code : | Sélectionner tout |
1 2 | - type NonNullable<T> = T extends null | undefined ? never : T;
+ type NonNullable<T> = T & {}; |
Il s'agit d'une amélioration car les types d'intersection comme celui-ci peuvent être réduits et assignés, alors que les types conditionnels ne le peuvent pas actuellement. Ainsi, NonNullable<NonNullable<T>> se simplifie maintenant au moins en NonNullable<T>, alors que ce n'était pas le cas auparavant.
| Code : | Sélectionner tout |
1 2 3 4 | function foo<T>(x: NonNullable<T>, y: NonNullable<NonNullable<T>>) {
x = y; // always worked
y = x; // used to error, now works
} |
Ces changements nous ont également permis d'apporter des améliorations sensibles à l'analyse du flux de contrôle et à la réduction des types. Par exemple, le type inconnu est maintenant réduit comme {} | null | undefined dans les branches véridiques.
| Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | function narrowUnknownishUnion(x: {} | null | undefined) {
if (x) {
x; // {}
}
else {
x; // {} | null | undefined
}
}
function narrowUnknown(x: unknown) {
if (x) {
x; // used to be 'unknown', now '{}'
}
else {
x; // unknown
}
} |
Les valeurs génériques sont également limitées de la même manière. Lorsqu'il vérifie qu'une valeur n'est pas nulle ou indéfinie, TypeScript la croise désormais avec {} - ce qui revient à dire qu'elle est NonNullable. En combinant les nombreux changements, nous pouvons maintenant définir la fonction suivante sans aucune assertion de type.
| Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | function throwIfNullable<T>(value: T): NonNullable<T> {
if (value === undefined || value === null) {
throw Error("Nullable value!");
}
// Used to fail because 'T' was not assignable to 'NonNullable<T>'.
// Now narrows to 'T & {}' and succeeds because that's just 'NonNullable<T>'.
return value;
} |
se réduit désormais à T & {}, et est désormais identique à NonNullable<T> - le corps de la fonction fonctionne donc sans syntaxe spécifique à TypeScript. En soi, ces changements peuvent sembler mineurs - mais ils représentent des corrections pour de nombreuses coupures de papier qui ont été signalées pendant plusieurs années.
Inférence améliorée pour les types d'inférence dans les types de chaînes de caractères de modèle
TypeScript a récemment introduit un moyen d'ajouter des contraintes étendues pour inférer les variables de type dans les types conditionnels.
| Code : | Sélectionner tout |
1 2 3 4 | // Grabs the first element of a tuple if it's assignable to 'number',
// and returns 'never' if it can't find one.
type TryGetNumberIfFirst<T> =
T extends [infer U extends number, ...unknown[]] ? U : never; |
Si ces types déduits apparaissent dans un modèle de type chaîne et sont contraints à un type primitif, TypeScript essaiera désormais d'analyser un type littéral.
| Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 | // SomeNum used to be 'number'; now it's '100'.
type SomeNum = "100" extends `${infer U extends number}` ? U : never;
// SomeBigInt used to be 'bigint'; now it's '100n'.
type SomeBigInt = "100" extends `${infer U extends bigint}` ? U : never;
// SomeBool used to be 'boolean'; now it's 'true'.
type SomeBool = "true" extends `${infer U extends boolean}` ? U : never; |
| Code : | Sélectionner tout |
1 2 | // JustNumber is `number` here because TypeScript parses out `"1.0"`, but `String(Number("1.0"))` is `"1"` and doesn't match.
type JustNumber = "1.0" extends `${infer T extends number}` ? T : never; |
Améliorations des performances de --build, --watch, et --incremental
TypeScript 4.8 introduit plusieurs optimisations qui devraient accélérer les scénarios autour de --watch et --incremental, ainsi que les constructions de références de projet utilisant --build. Par exemple, TypeScript est maintenant capable d'éviter de passer du temps à mettre à jour les timestamps pendant les changements no-op en mode --watch, ce qui rend les reconstructions plus rapides et évite de perturber les autres outils de construction qui pourraient être à l'affût de la sortie de TypeScript. De nombreuses autres optimisations permettant de réutiliser les informations entre --build, --watch et --incremental ont également été introduites.
Quelle est l'importance de ces améliorations ? Eh bien, sur une base de code interne assez importante, nous avons constaté des réductions de temps de l'ordre de 10 à 25 % sur de nombreuses opérations communes simples, avec des réductions de temps d'environ 40 % dans des scénarios sans changement. Nous avons également obtenu des résultats similaires sur la base de code TypeScript.
Erreurs lors de la comparaison des littéraux d'objets et de tableaux
Dans de nombreux langages, des opérateurs comme == effectuent ce que l'on appelle une égalité de « valeur » sur les objets. Par exemple, en Python, il est possible de vérifier si une liste est vide en vérifiant si une valeur est égale à la liste vide en utilisant ==.
| Code : | Sélectionner tout |
1 2 3 | if people_at_home == []:
print("here's where I lie, broken inside. </3")
adopt_animals() |
Ce n'est pas le cas en JavaScript, où == et === entre objets (et donc, tableaux) vérifient si les deux références pointent vers la même valeur. Nous pensons qu'un code similaire en JavaScript est, au mieux, un coup de pouce pour les développeurs JavaScript, et au pire, un bogue dans le code de production. C'est pourquoi TypeScript interdit désormais le code suivant.
| Code : | Sélectionner tout |
1 2 3 4 5 6 | if (peopleAtHome === []) {
// ~~~~~~~~~~~~~~~~~~~
// This condition will always return 'false' since JavaScript compares objects by reference, not value.
console.log("here's where I lie, broken inside. </3")
adoptAnimals();
} |
Inférence améliorée à partir des modèles de liaison
Dans certains cas, TypeScript récupère un...
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.