Annonce de TypeScript 3.4 RC

Il y a quelques jours, nous avons annoncé la disponibilité de notre version candidate (RC) de TypeScript 3.4. Notre espoir est de recueillir les commentaires et les premiers problèmes pour garantir que notre version finale est simple à récupérer et à utiliser immédiatement.


Pour commencer à utiliser le RC, vous pouvez l'obtenir via NuGet ou utiliser npm avec la commande suivante:


npm install -g typescript@rc 

Vous pouvez également obtenir le support de l'éditeur en



Explorons les nouveautés de la 3.4!




Cet article dans notre blog.

--incremental ultérieures plus rapides avec l'indicateur --incremental


Étant donné que les fichiers TypeScript sont compilés, il introduit une étape intermédiaire entre l'écriture et l'exécution de votre code. L'un de nos objectifs est de minimiser le temps de construction compte tenu de toute modification de votre programme. Pour ce faire, vous pouvez exécuter TypeScript en mode --watch . Lorsqu'un fichier change en mode --watch , TypeScript est capable d'utiliser le graphique de dépendances précédemment construit de votre projet pour déterminer quels fichiers pourraient avoir été affectés et doivent être revérifiés et potentiellement réémis. Cela peut éviter une vérification complète de type et une nouvelle émission, ce qui peut être coûteux.


Mais il n'est pas réaliste de s'attendre à ce que tous les utilisateurs maintiennent un processus tsc --watch en cours de nuit pour avoir des versions plus rapides demain matin. Et les constructions à froid? Au cours des derniers mois, nous avons --watch à savoir s'il existe un moyen d'enregistrer les informations appropriées du mode --watch dans un fichier et de les utiliser de génération en génération.


TypeScript 3.4 introduit un nouvel indicateur appelé --incremental qui indique à TypeScript de sauvegarder les informations sur le graphique du projet de la dernière compilation. La prochaine fois que TypeScript est invoqué avec --incremental , il utilisera ces informations pour détecter le moyen le moins coûteux de vérifier le type et d'émettre des modifications dans votre projet.


 // tsconfig.json { "compilerOptions": { "incremental": true, "outDir": "./lib" }, "include": ["./src"] } 

Par défaut avec ces paramètres, lorsque nous 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é. Mais si c'est le cas, tsc essaiera d'utiliser ce fichier pour effectuer une vérification de type incrémentielle et mettre à jour nos fichiers de sortie.


Ces fichiers .tsbuildinfo peuvent être supprimés en toute sécurité et n'ont aucun impact sur notre code au moment de l'exécution - ils sont purement utilisés pour accélérer les compilations. Nous pouvons également les nommer comme bon nous semble et les placer où nous voulons à l'aide de l'indicateur --tsBuildInfoFile .


 // front-end.tsconfig.json { "compilerOptions": { "incremental": true, "tsBuildInfoFile": "./buildcache/front-end", "outDir": "./lib" }, "include": ["./src"] } 

Tant que personne d'autre n'essaie d'écrire dans le même fichier cache, nous devrions pouvoir profiter de versions à froid incrémentielles plus rapides.


Projets composites


Une partie de l'intention des projets composites ( tsconfig.json avec composite défini sur true ) est que les références entre différents projets peuvent être créé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'informations de génération sera basé sur le nom du fichier de sortie. Par exemple, si notre fichier JavaScript de sortie est ./output/foo.js , alors 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 le versioning


Bien que le fichier généré par --incremental soit JSON, le fichier n'est pas destiné à être utilisé par un autre outil. Nous ne pouvons fournir aucune garantie de stabilité pour son contenu, et en fait, notre politique actuelle est qu'une version de TypeScript ne comprendra pas les fichiers .tsbuildinfo générés à partir d'une autre version.


Améliorations pour ReadonlyArray et les tuples en readonly


TypeScript 3.4 facilite un peu l'utilisation de types de type tableau en lecture seule.


Une nouvelle syntaxe pour ReadonlyArray


Le type ReadonlyArray décrit les ReadonlyArray pouvant uniquement être lus. Toute variable avec un handle vers un ReadonlyArray ne peut pas ajouter, supprimer ou remplacer des éléments du tableau.


 function foo(arr: ReadonlyArray<string>) { arr.slice(); // okay arr.push("hello!"); // error! } 

Bien qu'il soit souvent ReadonlyArray utiliser ReadonlyArray sur Array à des fins d'intention, cela a souvent été un problème étant donné que les tableaux ont une syntaxe plus agréable. Plus précisément, 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 utilisant un nouveau modificateur en readonly pour les types de tableau.


 function foo(arr: readonly string[]) { arr.slice(); // okay arr.push("hello!"); // error! } 

tuples en readonly


TypeScript 3.4 introduit également un nouveau support pour les tuples en readonly . Nous pouvons préfixer n'importe quel type de tuple avec le mot clé readonly pour en faire un tuple en readonly , un peu comme nous le pouvons maintenant avec la syntaxe abrégée de tableau. Comme vous pouvez vous y attendre, contrairement aux tuples ordinaires dont les emplacements peuvent être écrits, les tuples en readonly ne permettent que la lecture à partir de ces positions.


 function foo(pair: readonly [string, string]) { console.log(pair[0]); // okay pair[1] = "hello!"; // error } 

De la même manière que les tuples ordinaires sont des types qui s'étendent depuis Array - un tuple avec des éléments de type T 1 , T 2 , ... T n s'étend depuis Array< T 1 | T 2 | ... T n > - les tuples en readonly sont des types qui s'étendent à partir de ReadonlyArray . Ainsi, un tuple en readonly avec les éléments T 1 , T 2 , ... T n s'étend de ReadonlyArray< T 1 | T 2 | ... T n > .


Modificateurs de type mappés en readonly readonly et tableaux en readonly


Dans les versions antérieures de TypeScript, nous généralisions 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 tuples de la même manière.


 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 comme le type d'utilitaire Readonly n'étaient en fait pas d'opérations sur les types de tableau et de tuple.


 // 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 en readonly d'un type mappé convertit automatiquement les types de type tableau en leurs homologues en readonly correspondants.


 // 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 pourriez écrire un type d'utilitaire comme le type mappé accessible en écriture qui supprime la readonly et qui reconvertirait les conteneurs de tableaux en readonly en leurs équivalents mutables.


 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]>; 

Avertissements


Malgré son apparence, le modificateur de type en readonly ne peut être utilisé que pour la syntaxe des types de tableau et des types de tuple. Ce n'est pas un opérateur de type général.


 let err1: readonly Set<number>; // error! let err2: readonly Array<boolean>; // error! let okay: readonly boolean[]; // works fine 

assertions const


Lors de la déclaration d'une variable ou d'une propriété mutable, TypeScript élargit souvent les valeurs pour s'assurer que nous pouvons attribuer des choses plus tard sans écrire de type explicite.


 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" été élargi à la string type avant de déduire un type pour x .


Une autre vue pourrait être de dire que x a le type littéral d'origine "hello" et que nous ne pouvons pas assigner "world" plus tard comme ceci:


 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.


 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'ensemble de notre programme).


Malheureusement, comme nous l'avons vu dans le dernier exemple, en JavaScript, les propriétés sont modifiables par défaut. Cela signifie que le langage élargit souvent les types de manière indésirable, nécessitant des types explicites à certains endroits.


 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; } 

Jusqu'à un certain point, c'est correct, 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ées assertions const . Sa syntaxe est une assertion de type avec const à la place du nom de type (par exemple 123 as 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, ne pas passer de "hello" à une string )
  • les littéraux d'objet obtiennent des propriétés en readonly
  • les littéraux de tableau deviennent des tuples en readonly

 // 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; 

En dehors des fichiers .tsx , la syntaxe d'assertion des crochets angulaires peut également être utilisée.


 // 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 autrement seraient utilisés uniquement pour suggérer l'immuabilité au compilateur peuvent souvent être omis.


 // 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 les annotations de type ci-dessus n'étaient pas nécessaires. L'affirmation const permis à TypeScript de prendre le type d'expression le plus spécifique.


Avertissements


Une chose à noter est que les assertions const ne peuvent être appliquées immédiatement que sur des expressions littérales simples.


 // 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 entièrement immuable.


 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 globalThis 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 de une valeur globale. Dans différents environnements, il existe différentes façons d'accéder à ce qui est effectivement la portée globale - global dans Node, window , self ou frames dans le navigateur, ou this à certains endroits en dehors du mode strict. Rien de tout cela n'est évident et laisse souvent les utilisateurs incertains s'ils écrivent du code correct.


TypeScript 3.4 introduit la prise en charge de la vérification de type du nouveau globalThis ECMAScript - une variable globale qui, bien, fait référence à la portée globale. Contrairement aux solutions ci-dessus, globalThis fournit un moyen standard d'accéder à la portée globale qui peut être utilisé dans différents environnements.


 // in a global file: let abc = 100; // Refers to 'abc' from above. globalThis.abc = 200; 

globalThis peut également globalThis si une variable globale a été déclarée ou non en la traitant comme une propriété en readonly lors de son accès.


 const answer = 42; globalThis.answer = 333333; // error! 

Il est important de noter que TypeScript ne transforme pas les références à globalThis lors de la compilation vers les anciennes versions d'ECMAScript. En tant que tel, à moins que vous ne cibliez des navigateurs à feuilles persistantes (qui prennent déjà en charge globalThis ), vous souhaiterez peut-être utiliser un polyfill approprié à la place.


Convertir en paramètres nommés


Parfois, les listes de paramètres commencent à devenir lourdes.


 function updateOptions( hue?: number, saturation?: number, brightness?: number, positionX?: number, positionY?: number positionZ?: number) { // .... } 

Dans l'exemple ci-dessus, il est beaucoup trop facile pour un appelant de mélanger l'ordre des arguments donnés. Un modèle JavaScript commun consiste à utiliser à la place un «objet options», afin que chaque option soit explicitement nommée et que l'ordre n'ait jamais d'importance. Cela émule une fonctionnalité que d'autres langues ont appelée «paramètres nommés».


 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 - nous fournissons également les fonctionnalités que les éditeurs utilisent pour des fonctionnalités riches telles que les complétions, la définition et les refactorisations. Dans TypeScript 3.4, notre stagiaire Gabriela Britto a implémenté un nouveau refactoring pour convertir les fonctions existantes pour utiliser ce modèle de «paramètres nommés».


Un refactoring appliqué à une fonction pour lui faire prendre des paramètres nommés.


Bien que nous puissions changer le nom de la fonctionnalité par notre version finale de 3.4 et nous pensons qu'il peut y avoir de la place pour une partie de l'ergonomie, nous aimerions que vous essayiez la fonctionnalité et nous donniez votre avis.


Changements de rupture


De niveau supérieur, this est maintenant tapé


Le type de niveau supérieur est désormais tapé comme typeof globalThis au lieu de any . En conséquence, vous pouvez recevoir des erreurs pour accéder à des valeurs inconnues à this sous noImplicitAny .


 // previously okay in noImplicitAny, now an error this.whargarbl = 10; 

Notez que le code compilé sous noImplicitThis ne noImplicitThis aucune modification ici.


Arguments de type générique propagés


Dans certains cas, l'inférence améliorée de TypeScript 3.4 peut produire des fonctions génériques plutôt que celles qui acceptent et renvoient leurs contraintes (généralement {} ).


 declare function compose<T, U, V>(f: (arg: T) => U, g: (arg: U) => V): (arg: T) => V; function list<T>(x: T) { return [x]; } function box<T>(value: T) { return { value }; } let f = compose(list, box); let x = f(100) // In TypeScript 3.4, 'x.value' has the type // // number[] // // but it previously had the type // // {}[] // // So it's now an error to push in a string. x.value.push("hello"); 

Une annotation de type explicite sur x peut supprimer l'erreur.


Et ensuite?


TypeScript 3.4 est notre première version qui a eu un plan d'itération décrivant nos plans pour cette version, qui est censée s'aligner sur notre feuille de route de 6 mois . Vous pouvez garder un œil sur ces deux éléments et sur notre page de feuille de route pour les travaux à venir.


En ce moment, nous avons hâte de connaître votre expérience avec le RC, alors essayez-le maintenant et faites-nous part de vos réflexions!

Source: https://habr.com/ru/post/fr443996/


All Articles