Microsoft annonce la disponibilité de TypeScript 3.2 qui s'accompagne de nombreuses nouveautés
Comme la prise en charge de BigInt

Le , par Stéphane le calme

175PARTAGES

12  0 
strictBindCallApply

TypeScript 3.2 introduit une vérification plus stricte de bind, call et apply. Mais qu'est ce que cela signifie en réalité ?

En JavaScript, bind, apply et call sont des méthodes sur des fonctions qui nous permettent de faire des choses comme lier this, appliquer partiellement des arguments, appeler des fonctions ayant une valeur différente pour this et appeler des fonctions ayant un tableau en arguments.

Malheureusement, à ses débuts, TypeScript n’était pas en mesure de modéliser ces fonctions, et bind, call et apply étaient tous typés pour prendre un nombre quelconque d'arguments et renvoyer any. De plus, les fonctions fléchées et les arguments rest / spread de ES2015 ont fourni à Microsoft une nouvelle syntaxe qui selon l’éditeur, permet d’exprimer plus facilement ce que certaines de ces méthodes font - et de manière plus efficace.

Rappelons qu’une expression de fonction fléchée (arrow function en anglais) permet d'avoir une syntaxe plus courte que les expressions de fonction et ne possède pas ses propres valeurs pour this, arguments, super, ou new.target. Les fonctions fléchées sont souvent anonymes et ne sont pas destinées à être utilisées pour déclarer des méthodes.

Néanmoins, suite à la demande, Microsoft a été conduit à revoir le sujet récemment. L’éditeur affirme avoir réalisé que deux fonctionnalités ouvraient les bonnes abstractions pour typer avec précision bind, call et apply sans codage en dur:
  • this type de paramètre à partir de TypeScript 2.0
  • Modélisation des listes de paramètres avec des types de n-uplets à partir de TypeScript 3.0

L’équipe explique que « La combinaison de ces deux éléments permet de s'assurer que nos utilisations de bind, call et apply sont plus strictement vérifiées lorsque nous utilisons un nouvel indicateur appelé strictBindCallApply. Lors de l'utilisation de ce nouvel indicateur, les méthodes sur les objets appelables sont décrites par un nouveau type global appelé CallableFunction, qui déclare des versions plus strictes des signatures pour bind, call et apply. De même, toutes les méthodes sur des objets constructibles (mais non appelables) sont décrites par un nouveau type global appelé NewableFunction ».

À titre d'exemple, nous pouvons voir comment Function.prototype.apply agit sous ce comportement:

Code TypeScript : Sélectionner tout
1
2
3
4
5
6
7
8
function foo(a: number, b: string): string { 
    return a + b; 
} 
  
let a = foo.apply(undefined, [10]);              // error: too few argumnts 
let b = foo.apply(undefined, [10, 20]);          // error: 2nd argument is a number 
let c = foo.apply(undefined, [10, "hello", 30]); // error: too many arguments 
let d = foo.apply(undefined, [10, "hello"]);     // okay! returns a string

Mises en garde

Un inconvénient de cette nouvelle fonctionnalité est qu’en raison de certaines limitations, bind, call et apply ne peuvent pas encore totalement modéliser les fonctions génériques ou les fonctions surchargées. Lorsque vous utilisez ces méthodes sur une fonction générique, les paramètres de type sont remplacés par le type d'objet vide ({}), et lorsqu'ils sont utilisés sur une fonction avec des surcharges, seule la dernière surcharge sera modélisée.

Objet étendu sur des types génériques

JavaScript prend en charge un moyen pratique de copier les propriétés existantes d'un objet existant dans un nouvel objet qui en sera donc l’extension. Pour étendre un objet existant dans un nouvel objet, vous définissez un élément avec trois points consécutifs (...) comme suit:

Code TypeScript : Sélectionner tout
1
2
3
4
5
let person = { name: "Daniel", location: "New York City" }; 
  
// My secret revealed, I have two clones! 
let shallowCopyOfPerson = { ...person }; 
let shallowCopyOfPersonWithDifferentLocation = { ...person, location: "Seattle" };

TypeScript fait un très bon travail ici lorsqu'il dispose de suffisamment d'informations sur le type. Le système de typage essaie de modéliser le comportement des extensions et surcharge de nouvelles propriétés, essaie d’ignorer les méthodes, etc. Mais jusqu’à présent, il ne fonctionnait pas du tout avec les génériques.

Code TypeScript : Sélectionner tout
1
2
3
4
function merge<T, U>(x: T, y: U) { 
    // Previously an error! 
    return { ...x, ...y }; 
}

Pour l’équipe, « C'était une erreur car nous n'avions aucun moyen d'exprimer le type de retour de merge. Il n'y avait pas de syntaxe (ni de sémantique) pouvant exprimer deux types inconnus qui allait en former un nouveau représentant leur extension.

« Nous aurions pu imaginer un nouveau concept dans le système de types appelé “type à extension d'objet”, et nous avions en fait une proposition à cet effet. Il s’agit essentiellement d’un nouvel opérateur de type qui ressemble à {... T, ... U} pour refléter la syntaxe d’un objet étendu ».

Lorsque T et U sont connus, ce type va s’écraser pour former un nouveau type d’objet.

Cependant, cela est assez complexe et nécessite l'ajout de nouvelles règles appliquées aux types pour des relations et des inférences. Alors que l’équipe a exploré plusieurs pistes, elle est récemment arrivée à deux conclusions:

  • Pour la plupart des utilisations des extensions en JavaScript, ils modélisaient bien le comportement avec les types d'intersection (c'est-à-dire Foo & Bar).
  • Object.assign - une fonction qui présente l’essentiel du comportement des extensions d’objets - est déjà modélisée à l’aide de types d’intersection, et l’équipe n’a constaté que très peu de réactions négatives à ce sujet.

Etant donné que les intersections modélisent les cas courants et que leur compréhension est relativement facile à la fois pour les utilisateurs et le système de types, TypeScript 3.2 autorise maintenant les extensions d’objets sur des génériques et les modélise à l’aide d’intersections:

Code TypeScript : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Returns 'T & U' 
function merge<T, U>(x: T, y: U) { 
    return { ...x, ...y }; 
} 
  
// Returns '{ name: string, age: number, greeting: string } & T' 
function foo<T>(obj: T) { 
    let person = { 
        name: "Daniel", 
        age: 26 
    }; 
  
    return { ...person, greeting: "hello", ...obj }; 
}

Héritage de configuration via les packages node_modules

Pendant longtemps, TypeScript a pris en charge l'extension des fichiers tsconfig.json à l'aide du champ extend.

Code TypeScript : Sélectionner tout
1
2
3
4
5
6
7
8
{ 
    "extends": "../tsconfig-base.json", 
    "include": ["./**/*"] 
    "compilerOptions": { 
        // Override certain options on a project-by-project basis. 
        "strictBindCallApply": false, 
    } 
}

Cette fonctionnalité est très utile pour éviter la duplication de configuration qui risquerait de faire perdre la synchronisation, mais elle fonctionne vraiment mieux lorsque plusieurs projets sont co-localisés dans le même référentiel afin que chaque projet puisse référencer une « base » commune tsconfig.json.

Toutefois, pour certaines équipes, les projets sont écrits et publiés comme des packages totalement indépendants. Ces projets ne disposent pas d’un fichier commun auquel ils peuvent faire référence. Par conséquent, les utilisateurs peuvent créer un package et une référence distincts:

Code TypeScript : Sélectionner tout
1
2
3
4
5
6
7
8
{ 
    "extends": "../node_modules/@my-team/tsconfig-base/tsconfig.json", 
    "include": ["./**/*"] 
    "compilerOptions": { 
        // Override certain options on a project-by-project basis. 
        "strictBindCallApply": false, 
    } 
}

Cependant, aller dans les répertoires parents avec une série de ../s et accéder directement à node_modules pour saisir un fichier spécifique semble un peu difficile.

TypeScript 3.2 résout maintenant tsconfig.jsons à partir de node_modules. Lors de l'utilisation d'un chemin nu pour le champ "extend" dans tsconfig.json, TypeScript plongera dans les packages node_modules à votre place.

Code TypeScript : Sélectionner tout
1
2
3
4
5
6
7
8
{ 
    "extends": "@my-team/tsconfig-base", 
    "include": ["./**/*"] 
    "compilerOptions": { 
        // Override certain options on a project-by-project basis. 
        "strictBindCallApply": false, 
    } 
}

Ici, TypeScript montera dans les dossiers node_modules à la recherche d'un paquet @ my-team / tsconfig-base. Pour chacun de ces packages, TypeScript vérifie d'abord si package.json contient un champ "tsconfig" et, le cas échéant, TypeScript essaiera de charger un fichier de configuration à partir de ce champ. Si aucun n'existe, TypeScript essaiera de lire à partir d'un fichier tsconfig.json à la racine. Cette procédure est similaire au processus de recherche des fichiers .js dans les packages utilisés par Node et au processus de recherche .d.ts déjà utilisé par TypeScript.

Cette fonctionnalité peut s'avérer extrêmement utile pour les grandes organisations ou les projets comportant de nombreuses dépendances distribuées.

Diagnostiquer tsconfig.json avec --showConfig

tsc, le compilateur TypeScript, prend en charge un nouvel indicateur appelé --showConfig. Lors de l'exécution de tsc --showConfig, TypeScript calculera le tsconfig.json effectif (après avoir calculé les options héritées du champ expand) et va afficher ce résultat. Cela peut être utile pour diagnostiquer les problèmes de configuration en général.

BigInt

BigInt fait partie d’une proposition à venir dans ECMAScript qui nous permet de modéliser des entiers théoriquement grands de façon arbitraire. TypeScript 3.2 apporte la vérification de type pour BigInt, ainsi que la prise en charge de l'émission de littéraux BigInt lors du ciblage de esnext.

La prise en charge de BigInt dans TypeScript introduit un nouveau type primitif appelé bigint (tout en minuscule). Vous pouvez obtenir un bigint en appelant la fonction BigInt () ou en écrivant un littéral BigInt en ajoutant un n à la fin de tout littéral numérique entier:

Code TypeScript : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let foo: bigint = BigInt(100); // the BigInt function 
let bar: bigint = 100n;        // a BigInt literal 
  
// *Slaps roof of fibonacci function* 
// This bad boy returns ints that can get *so* big! 
function fibonacci(n: bigint) { 
    let result = 1n; 
    for (let last = 0n, i = 0n; i < n; i++) { 
        const current = result; 
        result += last; 
        last = current; 
    } 
    return result; 
} 
  
fibonacci(10000n)

Bien que vous puissiez imaginer une interaction étroite entre number et bigint, ces deux domaines sont distincts.

Code TypeScript : Sélectionner tout
1
2
3
4
5
declare let foo: number; 
declare let bar: bigint; 
  
foo = bar; // error: Type 'bigint' is not assignable to type 'number'. 
bar = foo; // error: Type 'number' is not assignable to type 'bigint'.

Comme spécifié dans ECMAScript, le mélange de nombres et de bigints dans des opérations arithmétiques est une erreur. Vous devrez convertir explicitement les valeurs en BigInt.

Code TypeScript : Sélectionner tout
1
2
3
console.log(3.141592 * 10000n);     // error 
console.log(3145 * 10n);            // error 
console.log(BigInt(3145) * 10n);    // okay!

Il est également important de noter que les bigint produisent une nouvelle chaîne lors de l'utilisation de l'opérateur typeof: la chaîne "bigint".

Code TypeScript : Sélectionner tout
1
2
3
4
5
6
7
8
function whatKindOfNumberIsIt(x: number | bigint) { 
    if (typeof x === "bigint") { 
        console.log("'x' is a bigint!"); 
    } 
    else { 
        console.log("'x' is a floating-point number"); 
    } 
}

Mises en garde

Comme mentionné plus haut, le support BigInt n'est disponible que pour la cible esnext. Cela n’est peut-être pas évident, mais parce que BigInt a un comportement différent pour les opérateurs mathématiques tels que +, -, *, etc., fournir une fonctionnalité pour les cibles plus anciennes où la fonctionnalité n’existe pas (comme es2017 et les versions antérieures) impliquerait la réécriture de chacune de ces opérations. TypeScript aurait besoin d'affecter le comportement correct en fonction du type. Ainsi, tout ajout, concaténation de chaîne, multiplication, etc. impliquerait un appel de fonction.

Pour cette raison, Microsoft n’a aucun projet immédiat de fournir un support au niveau inférieur. L’équipe note tout de même que Node 11 et les versions plus récentes de Chrome prennent déjà en charge cette fonctionnalité. Vous pourrez donc utiliser BigInt dessus pour cibler esnext.

Source : Microsoft

Voir aussi :

TypeScript 3.1 est disponible, cette version s'accompagne des redirections de versions ainsi que des types de tableau et de tuple mappables
Google s'oriente vers TypeScript et voici pourquoi, selon Evan Martin, un ingénieur de la firme qui travaille sur le langage
TypeScript 3.0 est disponible en version stable : un aperçu des nouveautés de cette version majeure du surensemble typé de JavaScript
TypeScript 2.9 est disponible et intègre plusieurs nouveautés, le langage continue de compléter son système de typage

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

Avatar de kilroyFR
Membre éclairé https://www.developpez.com
Le 02/12/2018 à 10:08
Genial. Typescript m'a reconcilié depuis des années avec javascript et ses syntaxes barbares et c'est tres bien que M$ continue a le mettre en avant.
Avatar de Paleo
Membre confirmé https://www.developpez.com
Le 03/12/2018 à 11:28
Citation Envoyé par Stéphane le calme Voir le message
L’équipe note tout de même que Node 11 et les versions plus récentes de Chrome prennent déjà en charge cette fonctionnalité. Vous pourrez donc utiliser BigInt dessus pour cibler esnext.
En fait les BigInt sont implémentés sur les versions récentes de Node 10.
Avatar de Stéphane le calme
Chroniqueur Actualités https://www.developpez.com
Le 01/02/2019 à 7:40
Microsoft annonce la disponibilité de TypeScript 3.3 qui apporte une meilleure gestion des projets composites,
introduits par TypeScript 3.0

Amélioration du comportement des types d'appel d'union

Lorsque TypeScript a une union de type A | B, il vous permet d’accéder à toutes les propriétés communes à A et B (c’est-à-dire à l’intersection des membres).

Code TypeScript : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface A { 
    aProp: string; 
    commonProp: string; 
} 
  
interface B { 
    bProp: number; 
    commonProp: number 
} 
  
type Union = A | B; 
  
declare let x: Union; 
  
x.aProp; // error - 'B' doesn't have the property 'aProp' 
x.bProp; // error - 'A' doesn't have the property 'bProp' 
x.commonProp; // okay! Both 'A' and 'B' have a property named `commonProp`.

Ce comportement doit sembler intuitif: vous ne pouvez extraire une propriété d’un type d’union que si elle est connue pour appartenir à tous les types d’union.

Au lieu d’accéder aux propriétés, qu’en est-il des types d’appel? Eh bien, quand CHAQUE type a exactement une signature avec des paramètres identiques, tout fonctionne et vous pouvez appeler ces types.

Code TypeScript : Sélectionner tout
1
2
3
4
5
6
7
8
type CallableA = (x: boolean) => string; 
type CallableB = (x: boolean) => number; 
  
type CallableUnion = CallableA | CallableB; 
  
declare let f: CallableUnion; 
  
let x = f(true); // Okay! Returns a 'string | number'.

Cependant, cette restriction était parfois … trop restrictive.

Code TypeScript : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
type Fruit = "apple" | "orange"; 
type Color = "red" | "orange"; 
  
type FruitEater = (fruit: Fruit) => number;     // eats and ranks the fruit 
type ColorConsumer = (color: Color) => string;  // consumes and describes the colors 
  
declare let f: FruitEater | ColorConsumer; 
  
// Cannot invoke an expression whose type lacks a call signature. 
//   Type 'FruitEater | ColorConsumer' has no compatible call signatures.ts(2349) 
f("orange");

Exemple simple et faible message d'erreur mis à part, FruitEaters et ColorConsumers devraient pouvoir prendre la chaîne "orange" et renvoyer un nombre ou une chaîne.

Dans TypeScript 3.3, ce n'est plus une erreur.

Code TypeScript : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
type Fruit = "apple" | "orange"; 
type Color = "red" | "orange"; 
  
type FruitEater = (fruit: Fruit) => number;     // eats and ranks the fruit 
type ColorConsumer = (color: Color) => string;  // consumes and describes the colors 
  
declare let f: FruitEater | ColorConsumer; 
  
f("orange"); // It works! Returns a 'number | string'. 
  
f("apple");  // error - Argument of type '"apple"' is not assignable to parameter of type '"orange"'. 
  
f("red");    // error - Argument of type '"red"' is not assignable to parameter of type '"orange"'.

Dans TypeScript 3.3, les paramètres de ces signatures sont intersectés pour créer une nouvelle signature. Dans l'exemple ci-dessus, les paramètres fruit et color sont croisés ensemble pour former un nouveau paramètre de type Fruit & Color. Fruit & Color est vraiment identique à ("apple" | "orange") & ("red" | "orange") , ce qui équivaut à ("apple" & "red") | ("apple" & "orange") | ("orange" & "red") | ("orange" & "orange"). Chacune de ces intersections impossibles s’évapore, et nous nous retrouvons avec "orange" & "orange" , qui est juste "orange".

Il y a toujours quelques restrictions cependant. Ce nouveau comportement ne se déclenche que lorsque au plus un type de l'union comporte plusieurs surcharges et qu'au plus un type de l'union possède une signature générique. Cela signifie des méthodes sur le number[] | string[] comme map (qui est générique) ne sera toujours pas appelable.

D'autre part, des méthodes telles que forEach pourront désormais être appelées, mais sous noImplicitAny, certains problèmes peuvent survenir.

Code TypeScript : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface Dog { 
    kind: "pupper" 
    dogProp: any; 
} 
interface Cat { 
    kind: "kittyface" 
    catProp: any; 
} 
  
const catOrDogArray: Dog[] | Cat[] = []; 
  
catOrDogArray.forEach(animal => { 
    //                ~~~~~~ error! 
    // Parameter 'animal' implicitly has an 'any' type. 
});

Bien que Microsoft continue à améliorer l'expérience ici, cela est strictement plus efficace dans TypeScript 3.3, et l'ajout d'une annotation de type explicite fonctionnera.

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
interface Dog { 
    kind: "pupper" 
    dogProp: any; 
} 
interface Cat { 
    kind: "kittyface" 
    catProp: any; 
} 
  
const catOrDogArray: Dog[] | Cat[] = []; 
catOrDogArray.forEach((animal: Dog | Cat) => { 
    if (animal.kind === "pupper") { 
        animal.dogProp; 
        // ... 
    } 
    else if (animal.kind === "kittyface") { 
        animal.catProp; 
        // ... 
    } 
});

Surveillance incrémentielle des fichiers pour les projets composites dans --build --watch"]Surveillance incrémentielle des fichiers pour les projets composites dans --build --watch

Dans TypeScript 3.0, Microsoft a introduit une nouvelle fonctionnalité pour structurer les générations appelées «projets composites». Une partie de l'objectif ici était de s'assurer que les utilisateurs puissent diviser des projets volumineux en parties plus petites qui se construisent rapidement et préservent la structure du projet, sans compromettre l'expérience existante de TypeScript. Grâce aux projets composites, TypeScript peut utiliser le mode --build pour ne recompiler que l’ensemble des projets et des dépendances. Vous pouvez considérer cela comme une optimisation des générations entre projets.

Cependant, l’année dernière, l'équipe TypeScript a également fourni des versions optimisées du mode -- watch via une nouvelle API incrémentielle de «constructeur». Dans le même esprit, l'idée est que ce mode ne vérifie et ne réémet que les fichiers modifiés ou les fichiers dont les dépendances peuvent avoir une incidence sur la vérification du type. Vous pouvez considérer cela comme une optimisation des générations intra-projet.

Ironiquement, la construction de projets composites à l’aide de --build --watch n’a pas utilisé cette infrastructure. Une mise à jour dans un projet sous le mode --build --watch forcerait la génération complète de ce projet, plutôt que de déterminer quels fichiers de ce projet étaient affectés.

Dans TypeScript 3.3, l’indicateur --watch du mode de construction tire également parti de la surveillance incrémentielle des fichiers. Cela peut signifier des constructions beaucoup plus rapides sous --build --watch. Lors de nos tests, cette fonctionnalité a entraîné une réduction de 50% à 75% des temps de construction des temps de construction d'origine. Vous pouvez en savoir plus sur la demande d'extraction initiale du changement pour voir des numéros spécifiques, mais Microsoft pense que la plupart des utilisateurs de projets composites verront des gains importants ici.

Édition de JavaScript dans Sublime Text

Citation Envoyé par Microsoft
Grâce au travail de Zhengbo Li, ancien membre de l'équipe TypeScript, et de @idiotWu, contributeur à la communauté, notre plugin TypeScript pour Sublime Text prend désormais en charge la modification de fichiers JavaScript! Cela signifie que les utilisateurs obtiendront des complétions plus précises, renommeront, passeront à la définition, et plus encore, dans du code JavaScript qui utilise JSDoc et interagit avec le code TypeScript.


Source : Microsoft

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
Avatar de Sodium
Membre extrêmement actif https://www.developpez.com
Le 01/02/2019 à 8:35
Citation Envoyé par kilroyFR Voir le message
Genial. Typescript m'a reconcilié depuis des années avec javascript et ses syntaxes barbares et c'est tres bien que M$ continue a le mettre en avant.
Attention, il est très mal vu sur ce forum de pointer les lacunes de JavaScript.
JavaScript est un langage parfait et si tu n'apprécies pas c'est forcément parce que tu es un débutant qui n'a jamais étudié sérieusement le langage
Avatar de Stéphane le calme
Chroniqueur Actualités https://www.developpez.com
Le 18/03/2019 à 6:25
La Release Candidate de TypeScript 3.4 est disponible et apporte une nouvelle syntaxe pour ReadonlyArray,
ainsi que la vérification de type pour globalThis

Des builds subséquentes plus rapides avec le drapeau --incremental

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.

Citation Envoyé par Microsoft
Mais il est irréaliste d’attendre de tous les utilisateurs qu’un processus tsc -- watch se déroule pendant la nuit, afin d’obtenir des versions plus rapides le lendemain matin. Qu'en est-il des builds froides ? Au cours des derniers mois, nous nous sommes efforcés de déterminer s’il était possible de sauvegarder les informations appropriées du mode --watch dans un fichier et de les utiliser d’une build à l’autre.

TypeScript 3.4 introduit un nouvel indicateur appelé --incremental qui indique à TypeScript d'enregistrer des informations sur le graphe de projet de la dernière compilation. La prochaine fois que TypeScript est appelé avec --incremental, il utilisera ces informations pour détecter la méthode la moins coûteuse de vérification de type et d’émission de modifications de votre projet.

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"] 
}
Tant que personne n’essaye d’écrire dans le même fichier cache, nous devrions pouvoir profiter de versions froides incrémentielles plus rapides.

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, tout comme Date [] est un raccourci pour Array .

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
Contacter le responsable de la rubrique Accueil

Partenaire : Hébergement Web