« Aujourd'hui, nous sommes ravis d'annoncer notre Release Candidate (RC) de TypeScript 4.4 ! Entre maintenant et la version stable de TypeScript 4.4, nous n'attendons aucun autre changement en dehors des corrections de bogues critiques ».Types de propriétés facultatifs exacts (--exactOptionalPropertyTypes)
En JavaScript, la lecture d'une propriété manquante sur un objet produit la valeur undefined. Il est également possible d'avoir une propriété réelle avec la valeur undefined. Beaucoup de code en JavaScript a tendance à traiter ces situations de la même manière, et donc initialement, TypeScript a simplement interprété chaque propriété facultative comme si un utilisateur avait écrit undefined dans le type. Par exemple,
| Code TypeScript : | Sélectionner tout |
1 2 3 4 | interface Person { name: string, age?: number; } |
| Code TypeScript : | Sélectionner tout |
1 2 3 4 | interface Person { name: string, age?: number | undefined; } |
| Code TypeScript : | Sélectionner tout |
1 2 3 4 | const p: Person = { name: "Daniel", age: undefined, // This is okay by default. }; |
Dans TypeScript 4.4, le nouvel indicateur --exactOptionalPropertyTypes spécifie que les types de propriétés facultatifs doivent être interprétés exactement comme écrits, ce qui signifie que | undefined n'est pas ajouté au type*:
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 | // With 'exactOptionalPropertyTypes' on: const p: Person = { name: "Daniel", age: undefined, // Error! undefined isn't a number }; |
Blocs static dans les classes
TypeScript 4.4 prend en charge les blocs statiques dans les classes, une fonctionnalité ECMAScript à venir qui peut vous aider à écrire un code d'initialisation plus complexe pour les membres statiques.
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | class Foo { static count = 0; // This is a static block: static { if (someCondition()) { count++; } } } |
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Foo { static #count = 0; get count() { return this.#count; } static { try { const lastInstances = loadLastInstances(); count += lastInstances.length; } catch {} } } |
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // Prints: // 1 // 2 // 3 class Foo { static prop = 1 static { console.log(1); } static { console.log(2); } static { console.log(3); } } |
Analyse du flux de contrôle des conditions d'alias
En JavaScript, nous devons souvent sonder une variable de différentes manières pour voir si elle a un type plus spécifique que nous pouvons utiliser. TypeScript comprend ces vérifications et les appelle des gardes de type. Au lieu d'avoir à convaincre TypeScript du type d'une variable chaque fois que nous l'utilisons, le vérificateur de type s'appuie sur ce qu'on appelle l'analyse de flux de contrôle pour déduire le type dans chaque construction de langage.
Par exemple, nous pouvons écrire quelque chose comme :
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 | function foo(arg: unknown) { if (typeof arg === "string") { // We know this is a string now. console.log(arg.toUpperCase()); } } |
Cependant, que se passe-t-il si nous déplaçons la condition vers une constante ?
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 | function foo(arg: unknown) { const argIsString = typeof arg === "string"; if (argIsString) { console.log(arg.toUpperCase()); // ~~~~~~~~~~~ // Error! Property 'toUpperCase' does not exist on type 'unknown'. } } |
Dans TypeScript 4.4, ce n'est plus le cas. L'exemple ci-dessus fonctionne sans erreur ! Lorsque TypeScript voit que nous testons une valeur constante, il effectuera un peu de travail supplémentaire pour voir s'il contient une protection de type. Si cette protection de type opère sur un const, une propriété readonly ou un paramètre non modifié, alors TypeScript est capable de restreindre cette valeur de manière appropriée.
Différentes sortes de conditions de garde de type sont préservées – pas seulement des vérifications typeof. Par exemple, les contrôles sur les syndicats discriminés fonctionnent comme un charme.
| 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 area(shape: Shape): number { const isCircle = shape.kind === "circle"; if (isCircle) { // We know we have a circle here! return Math.PI * shape.radius ** 2; } else { // We know we're left with a square here! return shape.sideLength ** 2; } } |
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | function doSomeChecks( inputA: string | undefined, inputB: string | undefined, shouldDoExtraWork: boolean, ) { let mustDoWork = inputA && inputB && shouldDoExtraWork; if (mustDoWork) { // Can access 'string' properties on both 'inputA' and 'inputB'! const upperA = inputA.toUpperCase(); const upperB = inputB.toUpperCase(); // ... } } |
Une caractéristique intéressante ici est que cette analyse fonctionne de manière transitive. Si nous avons une constante affectée à une condition qui contient plus de constantes et que ces constantes sont chacune affectées à des gardes de type, alors TypeScript peut propager les conditions plus tard.
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | function f(x: string | number | boolean) { const isString = typeof x === "string"; const isNumber = typeof x === "number"; const isStringOrNumber = isString || isNumber; if (isStringOrNumber) { x; // Type of 'x' is 'string | number'. } else { x; // Type of 'x' is 'boolean'. } } |
Cette fonctionnalité devrait permettre à beaucoup de code JavaScript intuitif de « fonctionner » dans TypeScript sans que cela ne vous gêne.
Signatures d'index Template String Pattern et symbol
TypeScript nous permet de décrire des objets où chaque propriété doit avoir un certain type à l'aide de signatures d'index. Cela nous permet d'utiliser ces objets comme des types de type dictionnaire, où nous pouvons utiliser des clés de chaîne pour les indexer avec des crochets.
Par exemple, nous pouvons écrire un type avec une signature d'index qui accepte les clés de chaîne et correspond à des valeurs booléennes. Si nous essayons d'attribuer autre chose qu'une valeur booléenne, nous obtiendrons une erreur.
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 | interface BooleanDictionary { [key: string]: boolean; } declare let myDict: BooleanDictionary; // Valid to assign boolean values myDict["foo"] = true; myDict["bar"] = false; // Error, "oops" isn't a boolean myDict["baz"] = "oops"; |
De même, Array<T> définit déjà une signature d'index numérique qui nous permet d'insérer/récupérer des valeurs de type T.
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // This is part of TypeScript's definition of the built-in Array type. interface Array<T> { [index: number]: T; // ... } let arr = new Array<string>(); // Valid arr[0] = "hello!"; // Error, expecting a 'string' value here arr[1] = 123; |
TypeScript 4.4 résout ces limitations et autorise les signatures d'index pour les symboles et les modèles de chaînes de modèles.
Par exemple, TypeScript nous permet désormais de déclarer un type dont la clé peut être sur des symbol arbitraires.
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | interface Colors { [sym: symbol]: number; } const red = Symbol("red"); const green = Symbol("green"); const blue = Symbol("blue"); let colors: Colors = {}; colors[red] = 255; // Assignment of a number is allowed let redVal = colors[red]; // 'redVal' has the type 'number' colors[blue] = "da ba dee"; // Error: Type 'string' is not assignable to type 'number'. |
| 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 21 22 23 | interface Options { width?: number; height?: number; } let a: Options = { width: 100, height: 100, "data-blah": true, // Error! 'data-blah' wasn't declared in 'Options'. }; interface OptionsWithDataProps extends Options { // Permit any property starting with 'data-'. [optName: `data-${string}`]: unknown; } let b: OptionsWithDataProps = { width: 100, height: 100, "data-blah": true, // Works! "unknown-property": true, // Error! 'unknown-property' wasn't declared in 'OptionsWithDataProps'. }; |
- string
- number
- symbol
- template de chaîne de modèle (par exemple `hello-${string}`)
Une signature d'index dont l'argument est une union de ces types se détendra en plusieurs signatures d'index différentes.
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | interface Data { [optName: string | symbol]: any; } // Equivalent to interface Data { [optName: string]: any; [optName: symbol]: any; } |
En JavaScript, tout type de valeur peut être lancé avec throw et capturé dans une clause catch. Pour cette raison, TypeScript a historiquement typé les variables de clause catch comme any et n'autorisait aucune autre annotation de type*:
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 | try { // Who knows what this might throw... executeSomeThirdPartyCode(); } catch (err) { // err: any console.error(err.message); // Allowed, because 'any' err.thisWillProbablyFail(); // Allowed, because 'any' :( } |
C'est pourquoi TypeScript 4.4 introduit un nouvel indicateur appelé --useUnknownInCatchVariables. Cet indicateur modifie le type par défaut des variables de clause catch de any à unknown.
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | try { executeSomeThirdPartyCode(); } catch (err) { // err: unknown // Error! Property 'message' does not exist on type 'unknown'. console.error(err.message); // Works! We can narrow 'err' from 'unknown' to 'Error'. if (err instanceof Error) { console.error(err.message); } } |
Vous avez lu gratuitement 290 articles depuis plus d'un an.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.