Comme les fichiers TypeScript sont compilés, cela introduit une étape intermédiaire entre l'écriture et l'exécution de votre code. L'un des objectifs de Microsoft est de minimiser le temps passé en cas de modification de votre programme. Une façon de faire est d’exécuter TypeScript en mode --watch. Lorsqu’un fichier est modifié sous le mode --watch, TypeScript peut utiliser le graphe de dépendances précédemment construit de votre projet pour déterminer les fichiers susceptibles d’avoir été affectés et devant faire l’objet d’une nouvelle vérification et éventuellement d’une nouvelle émission. Cela peut éviter une vérification de type complète et une réémission qui peut être coûteuse.
Envoyé par Microsoft
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 | // tsconfig.json { "compilerOptions": { "incremental": true, "outDir": "./lib" }, "include": ["./src"] } |
Par défaut, avec ces paramètres, lors de l’exécution de tsc, TypeScript recherchera un fichier appelé .tsbuildinfo dans notre répertoire de sortie (./lib). Si ./lib/.tsbuildinfo n’existe pas, il sera généré. Si tel est le cas, tsc essaiera d’utiliser ce fichier pour vérifier et mettre à jour de manière incrémentielle nos fichiers de sortie.
Ces fichiers .tsbuildinfo peuvent être supprimés en toute sécurité et n’ont aucune incidence sur notre code au moment de l’exécution - ils sont uniquement utilisés pour accélérer les compilations. Nous pouvons également leur attribuer le nom de notre choix et les placer où bon nous semble en utilisant le drapeau --tsBuildInfoFile.
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | // front-end.tsconfig.json { "compilerOptions": { "incremental": true, "tsBuildInfoFile": "./buildcache/front-end", "outDir": "./lib" }, "include": ["./src"] } |
Projets composites
Une partie de l'intention avec les projets composites (tsconfig.jsons avec composite défini sur true) est que les références entre différents projets peuvent être générées de manière incrémentielle. En tant que tels, les projets composites produiront toujours des fichiers .tsbuildinfo.
outFile
Lorsque outFile est utilisé, le nom du fichier d’information de la build sera basé sur le nom du fichier de sortie. Par exemple, si notre fichier JavaScript de sortie est ./output/foo.js, sous l'indicateur --incremental, TypeScript générera le fichier ./output/foo.tsbuildinfo. Comme ci-dessus, cela peut être contrôlé avec l'indicateur --tsBuildInfoFile.
Le format de fichier --incremental et la gestion des versions
Bien que le fichier généré par --incremental soit JSON, le fichier n’est pas destiné à être utilisé par un autre outil. Microsoft ne peut donc fournir aucune garantie de stabilité pour son contenu et, en fait, sa politique actuelle est qu'une version quelconque de TypeScript ne comprendra pas les fichiers .tsbuildinfo générés à partir d'une autre version.
Améliorations pour les tuples ReadonlyArray et Readonly
TypeScript 3.4 facilite l'utilisation des types de type tableau en lecture seule.
Une nouvelle syntaxe pour ReadonlyArray
Le type ReadonlyArray décrit les tableaux pouvant uniquement être lus. Toute variable associée à un ReadonlyArray ne peut ajouter, supprimer ou remplacer aucun élément du tableau.
Code TypeScript : | Sélectionner tout |
1 2 3 4 | function foo(arr: ReadonlyArray<string>) { arr.slice(); // okay arr.push("hello!"); // error! } |
Bien que ce soit souvent une bonne pratique d’utiliser ReadonlyArray sur Array à des fins d’intention, c’est souvent un problème, étant donné que les tableaux ont une syntaxe plus agréable. Spécifiquement, number [] est une version abrégée de Array<number>, tout comme Date [] est un raccourci pour Array <Date>.
TypeScript 3.4 introduit une nouvelle syntaxe pour ReadonlyArray en utilisant un nouveau modificateur readonly pour les types de tableau.
Code TypeScript : | Sélectionner tout |
1 2 3 4 | function foo(arr: readonly string[]) { arr.slice(); // okay arr.push("hello!"); // error! } |
tuples en lecture seule
TypeScript 3.4 introduit également un nouveau support pour les tuples en lecture seule. Nous pouvons préfixer n'importe quel type de tuple avec le mot clé readonly pour en faire un tuple en lecture seule, un peu comme nous pouvons le faire avec la syntaxe abrégée de tableau. Comme vous vous en doutez, contrairement aux n-uplets ordinaires dans lesquels nous disposons de la possibilité d’écriture à des positions données, les n-uplets en lecture seule permettent uniquement de lire à partir de ces positions.
Code TypeScript : | Sélectionner tout |
1 2 3 4 | function foo(pair: readonly [string, string]) { console.log(pair[0]); // okay pair[1] = "hello!"; // error } |
Modificateurs de type mappés en lecture seule et tableaux en lecture seule
Dans les versions antérieures de TypeScript, Microsoft généralisait les types mappés pour qu'ils fonctionnent différemment sur les types de type tableau. Cela signifiait qu'un type mappé comme Boxify pouvait fonctionner sur les tableaux et les n-uplets.
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | interface Box<T> { value: T } type Boxify<T> = { [K in keyof T]: Box<T[K]> } // { a: Box<string>, b: Box<number> } type A = Boxify<{ a: string, b: number }>; // Array<Box<number>> type B = Boxify<number[]>; // [Box<string>, Box<number>] type C = Boxify<[string, boolean]>; |
Malheureusement, les types mappés tels que le type utilitaire Readonly étaient en réalité des no-ops sur les types array et tuple.
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // lib.d.ts type Readonly<T> = { readonly [K in keyof T]: T[K] } // How code acted *before* TypeScript 3.4 // { readonly a: string, readonly b: number } type A = Readonly<{ a: string, b: number }>; // number[] type B = Readonly<number[]>; // [string, boolean] type C = Readonly<[string, boolean]>; |
Dans TypeScript 3.4, le modificateur readonly dans un type mappé convertira automatiquement les types de type tableau en leurs homologues en lecture seule correspondants.
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | // How code acts now *with* TypeScript 3.4 // { readonly a: string, readonly b: number } type A = Readonly<{ a: string, b: number }>; // readonly number[] type B = Readonly<number[]>; // readonly [string, boolean] type C = Readonly<[string, boolean]>; |
De même, vous pouvez écrire un type d’utilitaire tel que le type mappé Writable qui supprime la lecture seule et convertit les conteneurs de tableaux en lecture seule en leurs équivalents mutables.
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | type Writable<T> = { -readonly [K in keyof T]: T[K] } // { a: string, b: number } type A = Writable<{ readonly a: string; readonly b: number }>; // number[] type B = Writable<readonly number[]>; // [string, boolean] type C = Writable<readonly [string, boolean]>; |
Mises en garde
Malgré son apparence, le modificateur de type readonly ne peut être utilisé que pour la syntaxe sur les types de tableaux et les types de tuples. Ce n'est pas un opérateur de type généraliste.
Code TypeScript : | Sélectionner tout |
1 2 3 4 | let err1: readonly Set<number>; // error! let err2: readonly Array<boolean>; // error! let okay: readonly boolean[]; // works fine |
affirmations const
Lors de la déclaration d'une variable ou d'une propriété mutable, TypeScript élargit souvent les valeurs pour garantir que nous puissions affecter des éléments ultérieurement sans écrire de type explicite.
Code TypeScript : | Sélectionner tout |
1 2 3 4 | let x = "hello"; // hurray! we can assign to 'x' later on! x = "world"; |
Techniquement, chaque valeur littérale a un type littéral. Ci-dessus, le type "hello" s'est élargi à la chaîne de type avant d'inférer un type pour x.
Une autre perspective pourrait être de dire que x a le type littéral original "hello" et que nous ne pouvons pas assigner "world" plus tard, comme ceci:
Code TypeScript : | Sélectionner tout |
1 2 3 4 | let x: "hello" = "hello"; // error! x = "world"; |
Dans ce cas, cela semble extrême, mais cela peut être utile dans d’autres situations. Par exemple, les TypeScripters créent souvent des objets destinés à être utilisés dans des unions discriminées.
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | type Shape = | { kind: "circle", radius: number } | { kind: "square", sideLength: number } function getShapes(): readonly Shape[] { let result = [ { kind: "circle", radius: 100, }, { kind: "square", sideLength: 50, }, ]; // Some terrible error message because TypeScript inferred // 'kind' to have the type 'string' instead of // either '"circle"' or '"square"'. return result; } |
La mutabilité est l'une des meilleures heuristiques d'intention que TypeScript peut utiliser pour déterminer quand élargir (plutôt que d'analyser l'intégralité de notre programme).
Malheureusement, comme nous l'avons vu dans le dernier exemple, les propriétés en JavaScript sont mutables par défaut. Cela signifie que le langage élargira souvent les types de manière indésirable, nécessitant des types explicites à certains endroits.
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | function getShapes(): readonly Shape[] { // This explicit annotation gives a hint // to avoid widening in the first place. let result: readonly Shape[] = [ { kind: "circle", radius: 100, }, { kind: "square", sideLength: 50, }, ]; return result; } |
Cela peut aller jusqu'à un certain point, mais à mesure que nos structures de données deviennent de plus en plus complexes, cela devient lourd.
Pour résoudre ce problème, TypeScript 3.4 introduit une nouvelle construction pour les valeurs littérales appelée assertions const. Sa syntaxe est une assertion de type avec const à la place du nom du type (par exemple, 123 en tant que const). Lorsque nous construisons de nouvelles expressions littérales avec des assertions const, nous pouvons signaler au langage que
- aucun type littéral dans cette expression ne doit être élargi (par exemple, pas de passage de "hello" à string)
- les littéraux d'objet obtiennent des propriétés en lecture seule
- les littéraux de tableau deviennent des tuples en lecture seule
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // Type '10' let x = 10 as const; // Type 'readonly [10, 20]' let y = [10, 20] as const; // Type '{ readonly text: "hello" }' let z = { text: "hello" } as const; Outside of .tsx files, the angle bracket assertion syntax can also be used. // Type '10' let x = <const>10; // Type 'readonly [10, 20]' let y = <const>[10, 20]; // Type '{ readonly text: "hello" }' let z = <const>{ text: "hello" }; |
Cette fonctionnalité signifie souvent que les types qui seraient autrement utilisés pour suggérer une immuabilité au compilateur peuvent souvent être omis.
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 | // Works with no types referenced or declared. // We only needed a single const assertion. function getShapes() { let result = [ { kind: "circle", radius: 100, }, { kind: "square", sideLength: 50, }, ] as const; return result; } for (const shape of getShapes()) { // Narrows perfectly! if (shape.kind === "circle") { console.log("Circle radius", shape.radius); } else { console.log("Square side length", shape.sideLength); } } |
Notez que ce qui précède n'a pas besoin d'annotations de type. L'assertion de const a permis à TypeScript de prendre le type le plus spécifique de l'expression.
Mises en garde
Une chose à noter est que les assertions const ne peuvent être appliquées immédiatement que sur des expressions littérales simples.
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 | // Error! // A 'const' assertion can only be applied to a string, number, boolean, array, or object literal. let a = (Math.random() < 0.5 ? 0 : 1) as const; // Works! let b = Math.random() < 0.5 ? 0 as const : 1 as const; |
Une autre chose à garder à l’esprit est que les contextes const ne convertissent pas immédiatement une expression pour qu’elle soit totalement immuable.
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | let arr = [1, 2, 3, 4]; let foo = { name: "foo", contents: arr, }; foo.name = "bar"; // error! foo.contents = []; // error! foo.contents.push(5); // ...works! |
Vérification de type pour globalThis
Il peut être étonnamment difficile d’accéder ou de déclarer des valeurs dans la portée globale, peut-être parce que nous écrivons notre code dans des modules (dont les déclarations locales ne fuient pas par défaut), ou parce que nous pourrions avoir une variable locale qui masque le nom d’une valeur globale. Dans différents environnements, il existe différentes manières d’accéder à ce qui est effectivement la portée globale (global dans Node, window, self, ou frames dans le navigateur), ou bien à certains endroits en dehors du mode strict. Rien de tout cela n’est évident et laisse souvent les utilisateurs incertains s’ils écrivent le code correct.
TypeScript 3.4 prend en charge la vérification de type du nouveau globalThis d’ECMAScript - une variable globale qui, ainsi, fait référence à la portée globale. À la différence des solutions ci-dessus, globalThis fournit un moyen standard d'accéder à l'étendue globale pouvant être utilisée dans différents environnements.
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 | // in a global file: let abc = 100; // Refers to 'abc' from above. globalThis.abc = 200; |
globalThis peut également indiquer si une variable globale a été déclarée ou non en tant que const en la traitant comme une propriété en lecture seule lors de l'accès.
Code TypeScript : | Sélectionner tout |
1 2 3 | const answer = 42; globalThis.answer = 333333; // error! |
Il est important de noter que TypeScript ne transforme pas les références en globalThis lors de la compilation en versions antérieures d’ECMAScript. En tant que tel, à moins que vous ne cibliez des navigateurs permanents (qui prennent déjà en charge globalThis), vous souhaiterez peut-être utiliser un polyfill approprié.
Convertir en paramètres nommés
Parfois, les listes de paramètres commencent à devenir difficiles à manier.
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | function updateOptions( hue?: number, saturation?: number, brightness?: number, positionX?: number, positionY?: number positionZ?: number) { // .... } |
Dans l'exemple ci-dessus, il est trop facile pour un appelant de mélanger l'ordre des arguments donnés. Un modèle JavaScript courant consiste plutôt à utiliser un «objet options», de sorte que chaque option soit explicitement nommée et que l'ordre importe peu. Cela émule une fonctionnalité que d'autres langages ont appelée «paramètres nommés».
Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | interface Options { hue?: number, saturation?: number, brightness?: number, positionX?: number, positionY?: number positionZ?: number } function updateOptions(options: Options = {}) { // .... } |
L’équipe TypeScript ne travaille pas uniquement sur un compilateur (elle fournit également les fonctionnalités que les éditeurs utilisent pour les fonctionnalités riches telles que les complétions, la définition et les refactorings). Dans TypeScript 3.4, son stagiaire, Gabriela Britto, a mis en place un nouveau refactoring afin de convertir les fonctions existantes pour utiliser ce modèle de «paramètres nommés».
Source : TyoeScript
Voir aussi :
VS Code : Microsoft annonce la disponibilité d'IntelliCode pour le développement TypeScript/JavaScript, et espère améliorer la productivité
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
TypeScript 2.9 est disponible et intègre plusieurs nouveautés, le langage continue de compléter son système de typage