Fermer

mai 28, 2018

10 astuces et astuces essentielles de typeScript pour Angular Devs –


Dans cet article, nous allons plonger dans un ensemble de conseils et astuces qui devraient être utiles dans tous les projets Angular et au-delà quand il s'agit de TypeScript.

Ces dernières années, le besoin de taper statique en JavaScript a augmenté rapidement. Des projets frontaux croissants, des services plus complexes et des utilitaires de ligne de commande élaborés ont renforcé le besoin d'une programmation plus défensive dans le monde JavaScript. De plus, le fardeau de compiler une application avant de l'exécuter n'a pas été considéré comme une faiblesse, mais plutôt comme une opportunité. Alors que deux partis forts (TypeScript et Flow) ont émergé, beaucoup de tendances indiquent qu'un seul peut prévaloir – TypeScript.

Outre les revendications marketing et les propriétés communément connues, TypeScript a une communauté incroyable avec des contributeurs très actifs. Il a également l'une des meilleures équipes en termes de conception linguistique derrière elle. Sous la direction d'Anders Hejlsberg, l'équipe a réussi à transformer complètement le paysage des projets JavaScript à grande échelle pour en faire une entreprise presque exclusivement basée sur TypeScript. Avec des projets très réussis tels que VSTS ou Visual Studio Code, Microsoft est lui-même un fervent partisan de cette technologie.

Mais ce ne sont pas seulement les caractéristiques de TypeScript qui rendent le langage attrayant, mais aussi les possibilités et les frameworks. La décision de Google d'adopter pleinement TypeScript comme langue de prédilection pour Angular 2+ s'est avérée bénéfique pour tous. Non seulement TypeScript a gagné plus d'attention, mais aussi Angular lui-même. En utilisant le typage statique, le compilateur peut déjà nous donner des avertissements informatifs et des explications utiles sur la raison pour laquelle notre code ne fonctionnera pas.

Astuce TypeScript 1: Fournir vos propres définitions de module

TypeScript est un sur-ensemble de JavaScript. En tant que tel, chaque paquet npm existant peut être utilisé. Alors que l'écosystème TypeScript est énorme, toutes les bibliothèques ne sont pas encore livrées avec des typages appropriés. Pire encore, pour certains paquets (plus petits), même des déclarations séparées (sous la forme de @ types / {paquet} ) existent. À ce stade, nous avons deux options:

  1. introduire le code hérité en utilisant Tip TypeScript 7
  2. définir l'API du module nous-mêmes.

Ce dernier est définitivement préféré. Non seulement devons-nous regarder la documentation du module de toute façon, mais en le tapant, nous éviterons de simples erreurs au cours du développement. De plus, si nous sommes vraiment satisfaits des typages que nous venons de créer, nous pouvons toujours les soumettre à @types pour les inclure dans npm. En tant que tel, cela nous récompense également avec respect et gratitude de la part de la communauté. Nice!

Quel est le moyen le plus simple de fournir nos propres définitions de modules? Il suffit de créer un module.d.ts dans le répertoire source (ou il pourrait aussi être nommé comme le paquet – par exemple, unknown-module.d.ts pour un paquetage npm unknown-module ).

Donnons un exemple de définition pour ce module:

 declare module 'unknown-module' {
  const unknownModule: any;
  export = unknownModule;
}

Évidemment, ce n'est que la première étape, car nous ne devrions pas utiliser aucun du tout. (Il y a de nombreuses raisons à cela: l'astuce 5 de TypeScript montre comment l'éviter.) Cependant, il suffit d'enseigner le TypeScript sur le module et d'éviter les erreurs de compilation telles que "unknown module 'unknown-module'". La notation d'exportation ici est destinée au classique module.exports = ...

Voici la consommation potentielle en TypeScript d'un tel module:

 import * as unknownModule from 'unknown-module';

Comme déjà mentionné, toute la définition du module est maintenant placée dans la déclaration de type de la constante exportée. Si le contenu exporté est une fonction, la déclaration pourrait ressembler à ceci:

 déclare le module 'unknown-module' {
  interface UnknownModuleFunction {
    (): void;
  }
  const unknownModule: UnknownModuleFunction;
  export = unknownModule;
}

Bien sûr, il est également possible d'utiliser des paquets qui exportent des fonctionnalités en utilisant la syntaxe du module ES6:

 declare module 'unknown-module' {
  interface UnknownModuleFunction {
    (): void;
  }
  const unknownModule: UnknownModuleFunction;
  export const constantA: nombre;
  export const constanteB: chaîne;
  export par défaut unknownModule;
}

Astuce TypeScript 2: Enum vs Const Enum

TypeScript introduit le concept d'énumérations en JavaScript, qui représente une collection de constantes. La différence entre

 const Foo = {
  A: 1,
  B: 2,
}

et

 enum Foo {
  A = 1,
  B = 2,
}

n'est pas seulement de nature syntaxique en TypeScript. Tandis que les deux seront compilés à un objet (c.-à-d., Le premier restera tel quel, tandis que le second sera transformé par TypeScript), l'enum enum est protégé et ne contient que des membres constants. En tant que tel, il ne serait pas possible de définir ses valeurs pendant l'exécution. En outre, les modifications de ces valeurs ne seront pas autorisées par le compilateur TypeScript.

Cela se reflète également dans la signature. Ce dernier a une signature constante, qui est similaire à

 interface EnumFoo {
  A: 1;
  B: 2;
}

alors que l'objet est généralisé:

 interface ConstFoo {
  Un numéro;
  B: nombre;
}

Ainsi nous ne verrions pas les valeurs de ces "constantes" dans notre EDI. Qu'est-ce que const enum nous donne maintenant? Tout d'abord, regardons la syntaxe:

 const enum Foo {
  A = 1,
  B = 2,
}

C'est en fait la même chose – mais notez qu'il y a un const devant. Ce petit mot clé fait une énorme différence. Pourquoi? Parce que dans ces circonstances, TypeScript ne compilera rien. Nous avons donc la cascade suivante:

  • les objets sont intacts, mais génèrent une déclaration implicite de forme généralisée (interface)
  • enum génèrera des initialisations d'objet standard avec une déclaration de forme spécialisée
  • const enum ne génère rien à côté d'une déclaration de forme spécialisée

Comment ce dernier est-il alors utilisé dans le code? Par de simples remplacements. Considérez ce code:

 enum Foo {
  A = 1,
  B = 2
}

const enum Bar {
  A = 1,
  B = 2
}

console.log (Bar.A, Foo.B);

Ici, nous nous retrouvons en JavaScript avec le résultat suivant:

 var Foo;
(Fonction (Foo) {
  Foo [Foo["A"] = 1] = "A";
  Foo [Foo["B"] = 2] = "B";
}) (Foo || (Foo = {}));
console.log (1 / * A * /, Foo.B);

Notez que 5 lignes seules ont été générées pour enum Foo alors que enum Bar n'a abouti qu'à un simple remplacement (injection constante). Ainsi const enum est une fonctionnalité de compilation uniquement, alors que l'enum d'origine est une fonctionnalité runtime + compilation. La plupart des projets seront bien adaptés pour const enum mais il peut y avoir des cas où enum est préféré

TypeScript Astuce 3: Expressions de type

La plupart du temps, nous sommes satisfaits de l'utilisation de l'interface pour définir de nouvelles formes d'objets. Cependant, il y a des cas où une interface simple n'est plus suffisante. Considérez l'exemple suivant. Nous commençons avec une interface simple:

 interface StatusResponse {
  issues: Array ;
  statut: 'sain' | 'mauvais pour la santé';
}

La notation dans 'sain' | 'malsain' signifie soit une chaîne constante étant en bonne santé soit une autre chaîne constante égale à malsaine . D'accord, c'est une définition d'interface sonore. Cependant, maintenant nous avons aussi une méthode dans notre code, qui veut faire muter un objet de type StatusResponse :

 function setHealthStatus (état: 'sain' | 'malsain') {
  // ...
}

Jusqu'ici, tout va bien, mais en changeant cela maintenant 'sain' | «malsain» | 'unknown' entraîne déjà deux changements (un dans la définition de l'interface et un dans la définition du type d'argument dans la fonction). Pas cool. En fait, les expressions que nous avons regardées jusqu'à maintenant sont déjà des expressions de type, nous ne les avons simplement pas "stockées" – c'est-à-dire leur donner un nom (parfois appelé alias ). Faisons-le:

 type StatusResponseStatus = 'healthy' | 'mauvais pour la santé';

Alors que const var et permettent de créer des objets à l'exécution à partir d'expressions JS, type crée une déclaration de type lors de la compilation temps à partir des expressions TS (expressions dites de type). Ces déclarations de type peuvent ensuite être utilisées:

 interface StatusResponse {
  issues: Array ;
  statut: StatusResponseStatus;
}

Avec de tels alias dans notre ceinture à outils, nous pouvons facilement refactoriser le système de type à volonté. L'utilisation de l'inférence type de typeScript propage juste les changements en conséquence

TypeScript Astuce 4: Use Discriminators

L'une des utilisations des expressions de type est l'union précédemment introduite de plusieurs expressions (simples) de type – noms de type ou constantes . Bien sûr, l'union n'est pas limitée à des expressions de type simples, mais pour des raisons de lisibilité, nous ne devrions pas créer de structures comme celle-ci:

 type MyUnion = {
  a: booléen,
  b: numéro,
} | {
  c: nombre,
  ré: {
    sub: chaîne,
  }
} | {
  (): void;
}

Au lieu de cela, nous voulons une expression simple et directe, comme ceci:

 type MyUnion = TypeA | TypeB | TypeC;

Une telle union peut être utilisée comme une union dite discriminante si tous les types exposent au moins un membre avec le même nom, mais une valeur (constante) différente. Supposons que nous ayons trois types, tels que ceux-ci:

 interface Line {
  points: 2;
  // autres membres, par exemple de, à, ...
}

interface Triangle {
  points: 3;
  // autres membres, par exemple, centre, largeur, hauteur
}

interface Rectangle {
  points: 4;
  // autres membres, par exemple, haut, droite, bas, gauche
}

Une union discriminée entre ces types pourrait être la suivante:

 type Shape = Line | Triangle | Rectangle;

Ce nouveau type peut maintenant être utilisé dans des fonctions, où nous pouvons accéder à des membres spécifiques en utilisant une validation sur le discriminateur, qui serait la propriété points . Par exemple:

 function calcArea (shape: Shape) {
  switch (shape.points) {
    cas 2:
      // ... incl. revenir
    cas 3:
      // ... incl. revenir
    cas 4:
      // ... incl. revenir
    défaut:
      return Math.NaN;
  }
}

Naturellement, les déclarations switch sont très pratiques pour cette tâche, mais d'autres moyens de validation peuvent également être utilisés.

Les unions discriminées sont pratiques dans toutes sortes de scénarios – par exemple, lors de la traversée une structure semblable à AST qui traite les fichiers JSON qui ont un mécanisme de branchement similaire dans leur schéma

Astuce TypeScript 5: Éviter tout sauf si c'est vraiment

Nous avons tous été là: nous savons exactement quel code écrire, mais nous sommes incapables de satisfaire le compilateur TypeScript pour accepter notre modèle de données pour le code. Eh bien, heureusement pour nous, nous pouvons toujours revenir à n'importe quel pour avoir sauvé la journée. Mais nous ne devrions pas. any ne devrait être utilisé que pour les types qui peuvent en fait être quelconques. (Par exemple, c'est délibérément que JSON.parse renvoie any car le résultat pourrait être n'importe quoi en fonction de la chaîne que nous analysons.)

Par exemple, dans un de nos magasins de données, nous avons défini explicitement qu'un certain champ personnalisé contiendra des données de type any . Nous ne savons pas ce qui va s'y passer, mais le consommateur est libre de choisir les données (et donc le type de données). Nous ne voulions ni ne pouvions empêcher cela, donc le type any était réel

Cependant, dans la plupart des scénarios (c'est-à-dire dans tous les scénarios qui sont exclusivement couverts par notre code) any est généralement un ou plusieurs types. Nous avons seulement besoin de savoir quel type exactement nous attendons et comment construire un tel type pour donner à TypeScript toutes les informations nécessaires.

En utilisant certains des conseils précédents – par exemple, Astuce TypeScript 4 et Astuce TypeScript 3 – nous pouvons déjà résoudre certains des plus gros problèmes:

 function squareValue (x: any) {
  retourne Math.pow (x * 1, 2);
}

Nous préférerions beaucoup contraindre l'entrée autant que possible:

 function squareValue (x: string | number) {
  retourne Math.pow (+ x, 2);
}

Maintenant, la partie intéressante est que l'ancienne expression x * 1 est autorisée avec any mais rejetée en général. Cependant, le + x nous donne le cast forcé à un numéro comme voulu. Pour vérifier si notre distribution fonctionne avec les types donnés, nous devons être spécifiques. La question "quels types peuvent entrer ici?" Est une question légitime à laquelle nous devons répondre avant que TypeScript puisse nous fournir des informations utiles.

TypeScript Astuce 6: Utiliser Generics Efficacement

TypeScript signifie typage statique, mais typage statique doesn ne signifie pas typage explicite. TypeScript a une inférence de type puissante, qui doit être utilisée et entièrement comprise avant de pouvoir être vraiment productif dans TypeScript. Personnellement, je pense que je suis devenu beaucoup plus productif en langage JavaScript que JavaScript, car je ne passe pas beaucoup de temps sur mes dactylographes, mais tout semble être en place et presque toutes les erreurs triviales sont déjà détectées par TypeScript. L'un des moteurs de cette augmentation de la productivité est le générique. Generics nous donne la possibilité d'introduire des types en tant que variables.

Considérons le cas suivant d'une fonction d'assistance JS classique:

 function getOrUpdateFromCache (key, cb) {
  const valeur = getFromCache (clé);

  if (value === non défini) {
    const newValue = cb ();
    setInCache (clé, nouvelleValeur);
    return newValue;
  }

  valeur de retour;
}

Traduire ceci directement en TypeScript nous laisse derrière deux any s: l'un est la donnée récupérée du callback, et l'autre de la fonction elle-même. Cependant, cela n'a pas besoin de ressembler à cela, puisque nous connaissons évidemment le type (nous passons dans cb ):

 function getOrUpdateFromCache  (clé: chaîne, cb: () => T) {
  valeur const: T = getFromCache (clé);

  if (value === non défini) {
    const newValue = cb ();
    setInCache (clé, nouvelleValeur);
    return newValue;
  }

  valeur de retour;
}

La seule position gênante dans le code ci-dessus est l'affectation de type explicite au résultat de l'appel de la fonction getFromCache . Ici, nous devons faire confiance à notre code pour le moment pour toujours utiliser les mêmes types pour les mêmes clés. Dans tip tip 10, nous apprenons comment améliorer cette situation

La plupart du temps l'utilisation des génériques est juste pour "passer à travers" un type – c'est-à-dire, pour enseigner TypeScript sur la relation entre certains types d'arguments (dans l'ancien cas le type du résultat est connecté au type de retour du rappel). L'enseignement de TypeScript sur de telles relations peut également être soumis à d'autres contraintes, qui sont ensuite mises en place par TypeScript.

Bien que les génériques soient faciles à utiliser avec des interfaces, des types, des classes et des fonctions standard, ils peuvent sembler peu abordables. fonctions de flèche. Ces fonctions sont par définition anonymes (elles doivent être assignées à une variable accessible via un nom).

En règle générale, nous pouvons suivre cette approche: il suffit de penser à une déclaration de fonction normale, mais anonyme. Ici seulement le nom est parti. En tant que tel, le est naturellement placé juste avant les parenthèses. On aboutit à:

 const getOrUpdateFromCache =  (clé: chaîne, cb: () => T) => / * ... * /;

Cependant, une fois que nous aurions introduit ceci dans un fichier TSX (pour une raison quelconque), nous nous retrouverions avec une erreur ERROR: unclosed T tag . C'est le même problème qui apparaît avec les distributions (résolues là en utilisant l'opérateur comme ). Maintenant, notre solution consiste à dire explicitement à TypeScript que la syntaxe était destinée aux génériques:

 const getOrUpdateFromCache =  (clé: chaîne, cb: () => T) => / * ... * /;

Astuce TypeScript 7: Apporter du code hérité

La clé pour migrer du code existant vers TypeScript était un ensemble de paramètres de configuration TypeScript bien ajustés – par exemple, pour autoriser tout et pour désactiver mode strict. Le problème de cette approche est que le code transformé passe d'un état hérité à un état figé, ce qui a également un impact sur le nouveau code en cours d'écriture (puisque nous avons désactivé certaines des options de compilation les plus utiles).

utilisez allowJs dans le fichier tsconfig.json à côté des paramètres habituels (assez forts):

 {
  "compilerOptions": {
    "allowJs": vrai,
    // ...
  }
}

Maintenant, au lieu de renommer les fichiers existants de .js à .ts nous conservons les fichiers existants le plus longtemps possible. Nous ne renoncerons que si nous pouvons attaquer sérieusement le contenu de telle sorte que le code est entièrement transformé de JavaScript en une variante de typeScript qui satisfait nos paramètres.

Astuce TypeScript 8: Créer des fonctions avec des propriétés

l'utilisation d'interfaces pour déclarer la forme d'une fonction est une manière saine. De plus, cette approche nous permet d'attacher certaines propriétés au type de fonction donné. Voyons d'abord à quoi cela peut ressembler en pratique:

 interface PluginLoader {
  (): void;
  version: chaîne;
}

Définir cela est simple, mais malheureusement, travailler avec ne l'est pas. Essayons d'utiliser cette interface comme prévu en créant un objet qui remplit l'interface:

 const pl: PluginLoader = () => {};
pl.version = '1.0.0';

Ouch: nous ne pouvons pas dépasser la déclaration. TypeScript (correctement) se plaint que la propriété version est manquante. Ok, alors que diriez-vous de la solution de contournement suivante:

 interface PluginLoaderLight {
  (): void;
  version?: chaîne;
}

const pl: PluginLoaderLight = () => {};
pl.version = '1.0.0';

Parfait. Cela fonctionne, mais il a un inconvénient majeur: même si nous savons que passé l'affectation pl.version la propriété version existera toujours à pl TypeScript doesn Je ne sais pas. Donc, de son point de vue, tout accès à la version pourrait être erroné et doit être vérifié en premier undefined . En d'autres termes, dans la solution actuelle, l'interface que nous utilisons pour produire un objet de ce type doit être différente de l'interface utilisée pour la consommation. Ce n'est pas idéal.

Heureusement, il existe un moyen de contourner ce problème. Revenons à notre interface originale PluginLoader . Essayons avec un cast qui indique à TypeScript "Croyez-moi, je sais ce que je fais".

 const pl =  (() => {});
pl.version = '1.0.0';

Le but de ceci est de dire à TypeScript, "Voir cette fonction, je sais qu'elle sera de cette forme donnée ( PluginLoader )". TypeScript vérifie toujours si peut être encore rempli. Comme il n'y a pas de définitions de conflit disponibles, il acceptera cette distribution. Les lancers devraient être notre dernière ligne de défense. Je ne considère aucune ligne de défense possible: soit le type soit pour de vrai (peut toujours être – nous acceptons simplement n'importe quoi, totalement bien), ou il ne devrait pas être Elle peut être remplacée par quelque chose de spécifique (voir Astuce TypeScript 5).

Bien que la méthode de moulage puisse résoudre des problèmes tels que celui décrit, elle peut ne pas être réalisable dans un environnement non-angulaire (par exemple, ). Ici, nous devons choisir la variante alternative de cast, à savoir l'opérateur as :

 const pl = (() => {}) en tant que PluginLoader;
pl.version = '1.0.0';

Personnellement, j'opterais toujours pour en tant que moulage sous pression. Non seulement ils travaillent toujours mais ils sont également très lisibles même pour quelqu'un qui n'a pas d'arrière-plan de type. Pour moi, la cohérence et la lisibilité sont deux principes qui devraient toujours être au cœur de chaque base de code. Ils peuvent être cassés, mais il doit y avoir de bonnes raisons de le faire.

Astuce TypeScript 9: L'opérateur keyof

TypeScript est en fait assez bon pour les types de gestion. En tant que tel, il nous donne quelques armes qui peuvent être utilisées pour mettre en place un code pour générer le contenu d'une interface. De même, il nous offre également des options pour parcourir le contenu d'une interface.

Considérons l'interface suivante:

 interface AbstractControllerMap {
  utilisateur: UserControllerBase;
  données: DataControllerBase;
  paramètres: SettingsControllerBase;
  // ...
}

Potentiellement, dans notre code nous avons un objet avec une structure similaire. Les clés de cet objet sont magiques: ses cordes sont utilisées dans de nombreuses itérations et donc à plusieurs reprises. Il est très probable que nous utilisions ces clés comme arguments quelque part.

Évidemment, nous pourrions simplement indiquer qu'une fonction pourrait ressembler à ceci:

 function actOnAbstractController (controllerName: string) {
  // ...
}

L'inconvénient est que nous avons certainement plus de connaissances, que nous ne partageons pas avec TypeScript. Une meilleure version serait donc la suivante:

 function actOnAbstractController (controllerName: 'user' | 'data' | 'settings') {
  // ...
}

Cependant, comme déjà noté dans TypeScript 3, nous voulons être résilients face aux refactorings. Ce n'est pas résilient. Si nous ajoutons une autre clé (c'est-à-dire, mappons un autre contrôleur dans notre exemple ci-dessus), nous aurons besoin d'éditer le code à plusieurs endroits.

Une sortie sympa est fournie par l'opérateur de qui fonctionne contre tout type. Par exemple, l'aliasing des clés du AbstractControllerMap ci-dessus se présente comme suit:

 type ControllerNames = keyof AbstractControllerMap;

Maintenant, nous pouvons changer notre fonction pour devenir vraiment résiliente contre les refactorings sur la carte originale.

 function actOnAbstractController (controllerName: ControllerNames) {
  // ...
}

Ce qui est cool, c'est que keyof respectera réellement la fusion d'interfaces. Peu importe où nous plaçons la keyof cela fonctionnera toujours contre la version "finale" du type auquel elle est appliquée. Cela est également très utile lorsque vous envisagez des méthodes d'usine et une conception d'interface efficace pour eux

Astuce TypeScript 10: Définitions de rappel efficaces

Un problème qui apparaît plus souvent que prévu est le typage des gestionnaires d'événements. Regardons l'interface suivante pendant une seconde:

 interface MyEventEmitter {
  on (eventName: chaîne, cb: (e: any) => void): void;
  off (eventName: chaîne, cb: (e: any) => void): void;
  emit (eventName: string, event: any): void;
}

En revenant sur tous les tours précédents, nous savons que ce design n'est ni idéal ni acceptable. Alors, que pouvons-nous faire à ce sujet? Commençons par une simple approximation du problème. Une première étape consiste certainement à définir tous les noms d'événements possibles. Nous pourrions utiliser des expressions de type introduites dans le tip 3 de TypeScript, mais mieux encore, un mapping vers les déclarations de type d'évènement comme dans le précédent tip

Nous commençons donc avec notre map et appliquons le tip TypeScript 9 pour obtenir ce qui suit: 19659012] interface AllEvents {
  cliquez: tout;
  hover: tout;
  // …
}

tapez AllEventNames = keyof AllEvents;

Cela a déjà un effet. La définition de l'interface précédente devient:

 interface MyEventEmitter {
  on (eventName: AllEventNames, cb: (e: any) => void): void;
  off (eventName: AllEventNames, cb: (e: any) => void): void;
  emit (eventName: AllEventNames, event: any): void;
}

Un peu mieux, mais nous avons tout sur toutes les positions intéressantes. Maintenant, le tip 6 de TypeScript peut être appliqué pour que TypeScript soit un peu plus informé sur l'entrée eventName :

 interface MyEventEmitter {
  le  (eventName: T, cb: (e: tout) => void): void;
  off  (eventName: T, cb: (e: tout) => void): void;
  émettre  (eventName: T, event: any): void;
}

C'est bien, mais pas suffisant. TypeScript connaît maintenant le type exact de eventName lorsque nous l'entrons, mais nous ne pouvons pas utiliser l'information stockée dans T pour quelque chose. Sauf que nous pouvons l'utiliser avec d'autres expressions de type puissantes: les opérateurs d'index appliqués aux interfaces.

 interface MyEventEmitter {
  le  (eventName: T, cb: (e: AllEvents [T]) => void): void;
  off  (eventName: T, cb: (e: AllEvents [T]) => void): void;
  émettre  (eventName: T, événement: AllEvents [T]): void;
}

Cela semble être une chose puissante, sauf que nos déclarations existantes sont toutes réglées sur any . Changeons donc ceci.

 interface ClickEvent {
  leftButton: booléen;
  rightButton: booléen;
}

interface AllEvents {
  cliquez: ClickEvent;
  // ...
}

La partie vraiment puissante est maintenant que la fusion d'interface fonctionne encore. C'est-à-dire que nous pouvons étendre nos définitions d'événements en utilisant le même nom d'interface:

 interface AllEvents {
  Douane: {
    champ: chaîne;
  }
}

Cela rend les expressions de type encore plus puissantes, car l'extensibilité est intégrée de façon merveilleuse et élégante.

Lectures complémentaires

Conclusion

Espérons qu'un ou plusieurs de ces conseils de typeScript étaient nouveaux pour vous ou au moins quelque chose que vous vouliez voir de plus près. La liste est loin d'être complète, mais devrait vous donner un bon point de départ pour éviter certains problèmes et augmenter la productivité.

Quelles astuces font briller votre code? Où vous sentez-vous le plus à l'aise? Faites-le nous savoir dans les commentaires!




Source link