Imports et exports d'un type en particulierTypeScript réutilise la syntaxe d'importation de JavaScript afin de nous permettre de référencer les types. Par exemple, dans l'exemple suivant, nous pouvons importer doThing qui est une valeur JavaScript avec Options qui est purement un type TypeScript.
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // ./foo.ts interface Options { // ... } export function doThing(options: Options) { // ... } // ./bar.ts import { doThing, Options } from "./foo.js"; function doThingBetter(options: Options) { // do something twice as good doThing(options); doThing(options); } |
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | // ./foo.js export function doThing(options: Options) { // ... } // ./bar.js import { doThing } from "./foo.js"; function doThingBetter(options: Options) { // do something twice as good doThing(options); doThing(options); } |
| Code TypeScript : | Sélectionner tout |
1 2 3 | import { MyThing } from "./some-module.js"; export { MyThing }; |
L’autre problème était que l’élision d’importation de TypeScript supprimait les instructions d’importation qui ne contenaient que les importations utilisées comme types. Cela a provoqué un comportement sensiblement différent pour les modules qui ont des effets secondaires, et les utilisateurs devraient donc insérer une deuxième instruction d'importation uniquement pour contrer les effets secondaires.
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 | // This statement will get erased because of import elision. import { SomeTypeFoo, SomeOtherTypeBar } from "./module-with-side-effects"; // This statement always sticks around. import "./module-with-side-effects"; |
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 | // ./service.ts export class Service { // ... } register("globalServiceId", Service); // ./consumer.ts import { Service } from "./service.js"; inject("globalServiceId", function (service: Service) { // do stuff with Service }); |
Pour éviter ce type de problèmes, l'équipe a réalisé qu'elle devait donner aux utilisateurs un contrôle plus fin sur la façon dont les choses étaient importées/élidées.
En tant que solution dans TypeScript 3.8, l'équipe a ajouté une nouvelle syntaxe pour les importations et exportations de type uniquement.
| Code TypeScript : | Sélectionner tout |
1 2 3 | import type { SomeThing } from "./some-module.js"; export type { SomeThing }; |
Il est important de noter que les classes ont une valeur au moment de l'exécution et un type au moment du design, et l'utilisation est très sensible au contexte. Lorsque vous utilisez import type pour importer une classe, vous ne pouvez pas faire des choses comme faire une extension à partir de celle-ci.
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 | import type { Component } from "react"; interface ButtonProps { // ... } class Button extends Component<ButtonProps> { // ~~~~~~~~~ // error! 'Component' only refers to a type, but is being used as a value here. // ... } |
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 | // Is only 'Foo' a type? Or every declaration in the import? // We just give an error because it's not clear. import type Foo, { Bar, Baz } from "some-module"; // ~~~~~~~~~~~~~~~~~~~~~~ // error! A type-only import can specify a default import or named bindings, but not both. |
remove: c'est le comportement actuel de l'abandon de ces importations. Ce sera toujours la valeur par défaut.
preserve : cette valeur préserve toutes les importations dont les valeurs ne sont jamais utilisées. Cela peut entraîner la préservation des importations/effets secondaires.
error : cette valeur préserve toutes les importations (la même que l'option preserve), mais va provoquer une erreur lorsqu'une importation de valeur n'est utilisée que comme type. Cela peut être utile si vous voulez vous assurer qu'aucune valeur n'est importée accidentellement, tout en rendant les importations d'effets secondaires explicites.
Champs privés ECMAScript
TypeScript 3.8 prend en charge les champs privés d'ECMAScript, qui font partie de la proposition de champs de classe de stade 3.
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class Person { #name: string constructor(name: string) { this.#name = name; } greet() { console.log(`Hello, my name is ${this.#name}!`); } } let jeremy = new Person("Jeremy Bearimy"); jeremy.#name // ~~~~~ // Property '#name' is not accessible outside class 'Person' // because it has a private identifier. |
- Les champs privés commencent par un caractère #. Parfois, nous les appelons noms privés.
- Chaque nom de champ privé a une portée unique dans sa classe conteneur.
- Les modificateurs d'accessibilité TypeScript comme public ou private ne peuvent pas être utilisés sur des champs privés.
- Les champs privés ne peuvent pas être accédés ou même détectés en dehors de la classe contenant.
Les déclarations de propriétés régulières sont sujettes à être écrasées dans les sous-classes :
| 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 | class C { foo = 10; cHelper() { return this.foo; } } class D extends C { foo = 20; dHelper() { return this.foo; } } let instance = new D(); // 'this.foo' refers to the same property on each instance. console.log(instance.cHelper()); // prints '20' console.log(instance.dHelper()); // prints '20' |
| 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 | class C { #foo = 10; cHelper() { return this.#foo; } } class D extends C { #foo = 20; dHelper() { return this.#foo; } } let instance = new D(); // 'this.#foo' refers to a different field within each class. console.log(instance.cHelper()); // prints '10' console.log(instance.dHelper()); // prints '20' |
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Square { #sideLength: number; constructor(sideLength: number) { this.#sideLength = sideLength; } equals(other: any) { return this.#sideLength === other.#sideLength; } } const a = new Square(100); const b = { sideLength: 100 }; // Boom! // TypeError: attempted to get private field on non-instance // This fails because 'b' is not an instance of 'Square'. console.log(a.equals(b)); |
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | class C { // No declaration for '#foo' // :( constructor(foo: number) { // SyntaxError! // '#foo' needs to be declared before writing to it. this.#foo = foo; } } |
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | class C { /** @type {number} */ #foo; constructor(foo: number) { // This works. this.#foo = foo; } } |
Il est souvent courant d'avoir un point d'entrée unique qui expose tous les membres d'un autre module comme un seul membre.
| Code TypeScript : | Sélectionner tout |
1 2 | import * as utilities from "./utilities.js"; export { utilities }; |
| Code TypeScript : | Sélectionner tout |
export * as utilities from "./utilities.js";
Changements
TypeScript 3.8 contient quelques changements mineurs qui doivent être notés.
Contrôles d'assignation plus stricts aux unions avec des signatures d'index
Auparavant, les propriétés excédentaires n'étaient pas cochées lors de l'affectation à des unions où tout type avait une signature d'index - même si cette propriété excédentaire ne pouvait jamais satisfaire cette signature d'index. Dans TypeScript 3.8, le vérificateur de type est plus strict et « exempte » uniquement les propriétés des vérifications de propriétés excédentaires si cette propriété peut vraisemblablement satisfaire une signature d'index.
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | const obj1: { [x: string]: number } | { a: number }; obj1 = { a: 5, c: 'abc' } // ~ // Error! // The type '{ [x: string]: number }' no longer exempts 'c' // from excess property checks on '{ a: number }'. let obj2: { [x: string]: number } | { [x: number]: number }; obj2 = { a: 'abc' }; // ~ // Error! // The types '{ [x: string]: number }' and '{ [x: number]: number }' no longer exempts 'a' // from excess property checks against '{ [x: number]: number }', // and it *is* sort of an excess property because 'a' isn't a numeric property name. // This one is more subtle. |
Historiquement, la prise en charge de TypeScript pour la vérification de JavaScript a été laxiste à certains égards afin de fournir une expérience accessible.
Par exemple, les utilisateurs ont souvent utilisé Object dans JSDoc pour signifier « un objet, je ne sais pas quoi », il était alors traité comme any.
| Code TypeScript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | // @ts-check /** * @param thing {Object} some object, i dunno what */ function doSomething(thing) { let x = thing.x; let y = thing.y; thing(); } |
Cependant, TypeScript a un object nommé de type plus utile (notez que "o" est minuscule). Le type object est plus restrictif que Object, en ce sens qu'il rejette tous les types primitifs tels que string, boolean et number. Malheureusement, Object et object ont été traités comme any dans JSDoc.
Parce que object peut être utile et est utilisé beaucoup moins que Object dans JSDoc, l'équipe TypeScript a supprimé le comportement de cas spécial dans les fichiers JavaScript lors de l'utilisation de noImplicitAny afin que dans JSDoc, le type object se réfère vraiment au type d'objet non primitif.
Source : TypeScript
Vous avez lu gratuitement 36 078 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.