IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

TypeScript 4.9 RC est disponible et apporte les accesseurs automatiques dans les classes,
La surveillance des fichiers utilise maintenant les événements du système de fichiers

Le , par Bruno

22PARTAGES

6  0 
Depuis la publication de la version bêta le 25 septembre, TypeScript prend désormais en charge les accesseurs automatiques dans les classes. Dans les versions antérieures, TypeScript s'appuyait fortement sur l'interrogation pour suivre les fichiers individuels. Avec la release candidate (RC) de TypeScript 4.9, la surveillance des fichiers utilise maintenant les événements du système de fichiers et la façon dont TypeScript préserve les informations sur un type dans la branche vraie d'un type conditionnel a été optimisée. D'ici la version stable de TypeScript 4.9, Microsoft n'attend aucun autre changement en dehors des corrections de bogues critiques.


L'opérateur satisfies

Les développeurs TypeScript sont souvent confrontés à un dilemme : ils veulent s'assurer qu'une expression correspond à un certain type, mais ils voulent également conserver le type le plus spécifique de cette expression à des fins d'inférence. Par exemple :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
// Each property can be a string or an RGB tuple. 
const palette = { 
    red: [255, 0, 0], 
    green: "#00ff00", 
    bleu: [0, 0, 255] 
//  ^^^^ sacrebleu - we've made a typo! 
}; 
 
// We want to be able to use array methods on 'red'... 
const redComponent = palette.red.at(0); 
 
// or string methods on 'green'... 
const greenNormalized = palette.green.toUpperCase();

Remarquez que Microsoft écrit bleu, alors que nous elle aurait probablement dû écrire blue. Nous pourrions essayer de corriger la faute de frappe de bleu en utilisant une annotation de type sur palette, mais nous perdrions les informations sur chaque propriété.

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
type Colors = "red" | "green" | "blue"; 
 
type RGB = [red: number, green: number, blue: number]; 
 
const palette: Record<Colors, string | RGB> = { 
    red: [255, 0, 0], 
    green: "#00ff00", 
    bleu: [0, 0, 255] 
//  ~~~~ The typo is now correctly detected 
}; 
 
// But we now have an undesirable error here - 'palette.red' "could" be a string. 
const redComponent = palette.red.at(0);

Le nouvel opérateur satisfies nous permet de valider que le type d'une expression correspond à un certain type, sans modifier le type résultant de cette expression. Par exemple, nous pourrions utiliser satisfies pour valider que toutes les propriétés de palette sont compatibles avec string | number[] :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
type Colors = "red" | "green" | "blue"; 
 
type RGB = [red: number, green: number, blue: number]; 
 
const palette = { 
    red: [255, 0, 0], 
    green: "#00ff00", 
    bleu: [0, 0, 255] 
//  ~~~~ The typo is now caught! 
} satisfies Record<Colors, string | RGB>; 
 
// Both of these methods are still accessible! 
const redComponent = palette.red.at(0); 
const greenNormalized = palette.green.toUpperCase();

satisfies peut être utilisé pour visualiser de nombreuses erreurs possibles. Par exemple, nous pouvons nous assurer qu'un objet possède toutes les clés d'un certain type, mais pas plus :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
type Colors = "red" | "green" | "blue"; 
 
// Ensure that we have exactly the keys from 'Colors'. 
const favoriteColors = { 
    "red": "yes", 
    "green": false, 
    "blue": "kinda", 
    "platypus": false 
//  ~~~~~~~~~~ error - "platypus" was never listed in 'Colors'. 
} satisfies Record<Colors, unknown>; 
 
// All the information about the 'red', 'green', and 'blue' properties are retained. 
const g: boolean = favoriteColors.green;

Peut-être que nous ne nous soucions pas de savoir si les noms des propriétés correspondent d'une manière ou d'une autre, mais que nous nous intéressons aux types de chaque propriété. Dans ce cas, nous pouvons également nous assurer que toutes les valeurs des propriétés d'un objet sont conformes à un certain type.

[CODE]
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
type RGB = [red: number, green: number, blue: number]; 
 
const palette = { 
    red: [255, 0, 0], 
    green: "#00ff00", 
    blue: [0, 0] 
    //    ~~~~~~ error! 
} satisfies Record<string, string | RGB>; 
 
// Information about each property is still maintained. 
const redComponent = palette.red.at(0); 
const greenNormalized = palette.green.toUpperCase();

Restriction des propriétés non listées avec l'opérateur in

En tant que développeurs, nous devons souvent traiter des valeurs qui ne sont pas entièrement connues au moment de l'exécution. En fait, il arrive souvent que nous ne sachions pas si des propriétés existent, que nous obtenions une réponse d'un serveur ou que nous lisions un fichier de configuration. L'opérateur in de JavaScript peut vérifier si une propriété existe sur un objet.

Auparavant, TypeScript nous permettait d'éliminer tous les types qui ne listaient pas explicitement une propriété.

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface RGB { 
    red: number; 
    green: number; 
    blue: number; 
} 
 
interface HSV { 
    hue: number; 
    saturation: number; 
    value: number; 
} 
 
function setColor(color: RGB | HSV) { 
    if ("hue" in color) { 
        // 'color' now has the type HSV 
    } 
    // ... 
}
Ici, le type RGB n'a pas listé hueet a été restreint, nous laissant avec le type HSV. Mais qu'en est-il des exemples où aucun type ne mentionne une propriété donnée ? Dans ces cas-là, le langage ne nous a pas beaucoup aidés. Prenons l'exemple suivant en JavaScript :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
function tryGetPackageName(context) { 
    const packageJSON = context.packageJSON; 
    // Check to see if we have an object. 
    if (packageJSON && typeof packageJSON === "object") { 
        // Check to see if it has a string name property. 
        if ("name" in packageJSON && typeof packageJSON.name === "string") { 
            return packageJSON.name; 
        } 
    } 
 
    return undefined; 
}

La réécriture en TypeScript classique serait juste une question de définition et d'utilisation d'un type pour le contexte ; cependant, le choix d'un type sûr comme [C]unknown[C] pour la propriété packageJSON causerait des problèmes dans les anciennes versions de TypeScript.

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
interface Context { 
    packageJSON: unknown; 
} 
 
function tryGetPackageName(context: Context) { 
    const packageJSON = context.packageJSON; 
    // Check to see if we have an object. 
    if (packageJSON && typeof packageJSON === "object") { 
        // Check to see if it has a string name property. 
        if ("name" in packageJSON && typeof packageJSON.name === "string") { 
        //                                              ~~~~ 
        // error! Property 'name' does not exist on type 'object. 
            return packageJSON.name; 
        //                     ~~~~ 
        // error! Property 'name' does not exist on type 'object. 
        } 
    } 
 
    return undefined; 
}

En effet, alors que le type de packageJSON est passé d'inconnu à objet, l'opérateur in est strictement limité aux types qui définissent réellement la propriété vérifiée. Par conséquent, le type de packageJSON est resté objet.

TypeScript 4.9 rend l'opérateur in un peu plus puissant lorsqu'il s'agit de restreindre les types qui ne répertorient pas du tout la propriété. Au lieu de les laisser tels quels, le langage intersectera leurs types avec Record<"property-key-being-checked", unknown>. Ainsi, dans notre exemple, le type de packageJSON sera réduit de unknown à objet à objet & Record<"name", inconnu>. Cela nous permet d'accéder directement à packageJSON.name et de le réduire indépendamment.

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface Context { 
    packageJSON: unknown; 
} 
 
function tryGetPackageName(context: Context): string | undefined { 
    const packageJSON = context.packageJSON; 
    // Check to see if we have an object. 
    if (packageJSON && typeof packageJSON === "object") { 
        // Check to see if it has a string name property. 
        if ("name" in packageJSON && typeof packageJSON.name === "string") { 
            // Just works! 
            return packageJSON.name; 
        } 
    } 
 
    return undefined; 
}

TypeScript 4.9 renforce également quelques contrôles sur la façon dont in est utilisé, en s'assurant que le côté gauche est assignable au type string | number | symbol, et que le côté droit est assignable à object. Cela permet de vérifier que nous utilisons des clés de propriété valides et que nous ne vérifions pas accidentellement des primitives.

Accesseurs automatiques dans les classes

TypeScript 4.9 prend en charge une fonctionnalité à venir dans ECMAScript appelée auto-accesseurs. Les auto-accesseurs sont déclarés comme les propriétés des classes, sauf qu'ils sont déclarés avec le mot-clé accessor.

Code : Sélectionner tout
1
2
3
4
5
6
class Person { 
    accessor name: string; 
 
    constructor(name: string) { 
        this.name = name; 
    }

En dessous, ces auto-accesseurs se "désagrègent" en un accesseur get et set avec une propriété privée inaccessible.

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person { 
    #__name: string; 
 
    get name() { 
        return this.#__name; 
    } 
    set name(value: string) { 
        this.#__name = name; 
    } 
 
    constructor(name: string) { 
        this.name = name; 
    } 
} 
}

Contrôles d'égalité sur NaN

L'une des principales difficultés rencontrées par les développeurs JavaScript est la vérification de la valeur NaN à l'aide des opérateurs d'égalité intégrés. Pour rappel, NaN est une valeur numérique spéciale qui signifie "Not a Number" (pas un nombre). Rien n'est jamais égal à NaN - même NaN !

Code : Sélectionner tout
1
2
3
4
5
console.log(NaN == 0)  // false 
console.log(NaN === 0) // false 
 
console.log(NaN == NaN)  // false 
console.log(NaN === NaN) // false

Mais au moins symétriquement, tout est toujours non égal à NaN.

Code : Sélectionner tout
1
2
3
4
5
console.log(NaN != 0)  // true 
console.log(NaN !== 0) // true 
 
console.log(NaN != NaN)  // true 
console.log(NaN !== NaN) // true

Techniquement, ce n'est pas un problème spécifique à JavaScript, puisque n'importe quel langage qui contient des flottants IEEE-754 a le même comportement ; mais le principal type numérique de JavaScript est un nombre à virgule flottante, et l'analyse des nombres en JavaScript peut souvent aboutir à NaN. A son tour, la vérification contre les NaN finit par être assez commune, et la manière correcte de le faire est d'utiliser Number.isNaN - mais comme nous l'avons mentionné, beaucoup de gens finissent par vérifier avec someValue === NaN à la place.

TypeScript se trompe maintenant sur les comparaisons directes avec NaN, et suggère d'utiliser une variation de Number.isNaN à la place.

Code : Sélectionner tout
1
2
3
4
5
6
function validate(someValue: number) { 
    return someValue !== NaN; 
    //     ~~~~~~~~~~~~~~~~~ 
    // error: This condition will always return 'true'. 
    //        Did you mean '!Number.isNaN(someValue)'? 
}

Ce changement devrait aider à détecter les erreurs de débutants, de la même manière que TypeScript émet actuellement des erreurs sur les comparaisons avec les littéraux d'objets et de tableaux.

La surveillance des fichiers utilise maintenant les événements du système de fichiers

Dans les versions antérieures, TypeScript s'appuyait fortement sur l'interrogation pour suivre les fichiers individuels. L'utilisation d'une stratégie d'interrogation signifiait la vérification périodique de l'état d'un fichier pour les mises à jour. Sur Node.js, fs.watchFile est le moyen intégré d'obtenir un observateur de fichiers par échantillonnage. Bien que l'interrogation ait tendance à être plus prévisible sur les plateformes et les systèmes de fichiers, cela signifie que le CPU doit être interrompu périodiquement et vérifier les mises à jour du fichier, même si rien n'a changé. Pour quelques dizaines de fichiers, cela peut ne pas être perceptible, mais sur un projet plus important avec beaucoup de fichiers - ou beaucoup de fichiers dans les node_modules - cela peut devenir un monstre de ressources.

La plupart des plateformes modernes utilisées fournissent des facilités et des APIs comme CreateIoCompletionPort, kqueue, epoll et inotify. Node.js fait abstraction de tout cela en fournissant fs.watch. Les événements du système de fichiers fonctionnent généralement très bien, mais il y a beaucoup d'inconvénients à les utiliser et, par conséquent, à utiliser l'API fs.watch. Un observateur doit faire attention à prendre en compte la surveillance des inodes, l'indisponibilité de certains systèmes de fichiers (par exemple les systèmes de fichiers en réseau), si la surveillance récursive des fichiers est disponible, si les renommages de répertoires déclenchent des événements, et même l'épuisement des observateurs de fichiers ! En d'autres termes, ce n'est pas tout à fait un repas gratuit, surtout si vous recherchez quelque chose de multiplateforme.

Par conséquent, Microsoft a choisi par défaut le plus petit dénominateur commun : l'interrogation. Pas toujours, mais la plupart du temps.

Au fil du temps, Microsoft a fourni les moyens de choisir d'autres stratégies de surveillance des fichiers. « Cela nous a permis d'obtenir un retour d'information et de renforcer notre implémentation de file-watching contre la plupart de ces problèmes spécifiques à la plateforme », déclare l'équipe TypeScript. Comme TypeScript a dû s'adapter à des bases de code plus importantes, et s'est amélioré dans ce domaine, nous avons estimé que le passage aux événements du système de fichiers par défaut serait un investissement rentable.

Dans TypeScript 4.9, la surveillance des fichiers est alimentée par les événements du système de fichiers par défaut, ne revenant à l'interrogation que si Microsoft ne parvenons pas à mettre en place des surveillances basées sur des événements. Pour la plupart des développeurs, cela devrait fournir une expérience beaucoup moins gourmande en ressources lors de l'exécution en mode --watch, ou lors de l'exécution avec un éditeur alimenté par TypeScript comme Visual Studio ou VS Code.

La façon dont la surveillance des fichiers fonctionne peut toujours être configurée par le biais de variables d'environnement et d'options de surveillance, et certains éditeurs comme VS Code peuvent prendre en charge les options de surveillance de façon indépendante. Les développeurs utilisant des configurations plus exotiques où le code source réside sur un système de fichiers en réseau (comme NFS et SMB) peuvent avoir besoin de revenir à l'ancien comportement ; bien que si un serveur a une puissance de traitement raisonnable, il pourrait être préférable d'activer SSH et d'exécuter TypeScript à distance afin qu'il ait un accès direct aux fichiers locaux. VS Code dispose de nombreuses extensions à distance pour faciliter cette opération.

Commandes "Remove Unused Imports" et "Sort Imports" pour les éditeurs

Auparavant, TypeScript ne prenait en charge que deux commandes d'éditeur pour gérer les importations. Pour nos exemples, prenons le code suivant :

Code : Sélectionner tout
1
2
3
4
import { Zebra, Moose, HoneyBadger } from "./zoo"; 
import { foo, bar } from "./helper"; 
 
let x: Moose | HoneyBadger = foo();
La première s'appelle "Organize Imports" et supprime les importations inutilisées, puis trie celles qui restent. Il réécrit le fichier pour qu'il ressemble à celui-ci :

Code : Sélectionner tout
1
2
3
4
import { foo } from "./helper"; 
import { HoneyBadger, Moose } from "./zoo"; 
 
let x: Moose | HoneyBadger = foo();
Dans TypeScript 4.3, nous Microsoft a introduit une commande appelée "Sort Imports" qui ne fait que trier les importations dans le fichier, mais ne les supprime pas - et réécrit le fichier comme ceci.

Code : Sélectionner tout
1
2
3
4
import { bar, foo } from "./helper"; 
import { HoneyBadger, Moose, Zebra } from "./zoo"; 
 
let x: Moose | HoneyBadger = foo();
L'inconvénient de "Sort Imports" était que dans Visual Studio Code, cette fonctionnalité n'était disponible qu'en tant que commande on-save - et non en tant que commande déclenchable manuellement.

TypeScript 4.9 ajoute l'autre moitié, et fournit maintenant "Remove Unused Imports". TypeScript va maintenant supprimer les noms d'importation inutilisés et les déclarations, mais autrement laissera l'ordre relatif seul.

Code : Sélectionner tout
1
2
3
4
import { Moose, HoneyBadger } from "./zoo"; 
import { foo } from "./helper"; 
 
let x: Moose | HoneyBadger = foo();

Améliorations des performances

TypeScript a quelques petites, mais notables, améliorations de performance

Tout d'abord, la fonction forEachChild de TypeScript a été réécrite pour utiliser une consultation de table de fonctions au lieu d'une instruction switch sur tous les nœuds syntaxiques. forEachChild est un cheval de bataille pour traverser les nœuds syntaxiques dans le compilateur, et est fortement utilisé dans l'étape de liaison de notre compilateur, avec des parties du service de langue. Le remaniement de forEachChild a permis de réduire jusqu'à 20 % le temps passé dans notre phase de liaison et dans les opérations du service linguistique.

Après avoir découvert ce gain de performance pour forEachChild, nous l'avons essayé sur visitEachChild, une fonction que nous utilisons pour transformer les nœuds dans le compilateur et le service de langue. La même refactorisation a permis de réduire jusqu'à 3 % le temps passé à générer les résultats du projet. L'exploration initiale de forEachChild a été inspirée par un article de blog d'Artemis Everfree.Enfin, la façon dont TypeScript préserve les informations sur un type dans la branche vraie d'un type conditionnel a été optimisée. Dans un type comme

Code : Sélectionner tout
1
2
3
4
5
interface Zoo<T extends Animal> { 
    // ... 
} 
 
type MakeZoo<A> = A extends Animal ? Zoo<A> : never;

TypeScript doit « se souvenir » que A doit également être un animal lorsqu'il vérifie si Zoo<A> est valide. Cela se fait essentiellement en créant un type spécial qui contient l'intersection de A avec Animal ; cependant, TypeScript le faisait auparavant de manière empressée, ce qui n'est pas toujours nécessaire. En outre, un code défectueux dans le vérificateur de type a empêché ces types spéciaux d'être simplifiés.

TypeScript reporte maintenant l'intersection de ces types jusqu'à ce que cela soit nécessaire. Pour les bases de code avec une utilisation intensive des types conditionnels, vous pourriez assister à des accélérations significatives avec TypeScript, mais dans notre suite de tests de performance, nous avons vu une réduction plus modeste de 3 % du temps de vérification de type.

Source : Microsoft

Et vous ?

Que pensez-vous de TypeScript ?

Avez-vous déjà utilisé TypeScript ? Pour des projets personnels ou en entreprise ?

Si vous ne l'avez pas encore utilisé, prévoyez-vous de le faire ? Pourquoi ?

Quelles améliorations vous intéressent le plus ?

Voir aussi :

TypeScript a 10 ans ! Joyeux anniversaire. À cette occasion, Microsoft fait le point. L'entreprise revient sur les doutes des premiers jours ainsi que sur l'évolution du langage

TypeScript 4.8 bêta apporte des améliorations de performances à --watch et --build, mais empêche l'importation/l'exportation des types dans les fichiers JavaScript

TypeScript 4.8 RC permet d'exclure des fichiers spécifiques des importations automatiques et améliore l'inférence à partir de modèles de liaison

TypeScript 4.9 Beta est disponible et apporte la restriction des propriétés non listées avec l'opérateur in, ainsi que la vérification de l'égalité sur NaN

Une erreur dans cette actualité ? Signalez-nous-la !