TypeScript a un tas de façons différentes de définir une énumération:
enum Alpha { X, Y, Z }
const enum Beta { X, Y, Z }
declare enum Gamma { X, Y, Z }
declare const enum Delta { X, Y, Z }
Si j'essaie d'utiliser une valeur de Gamma
au moment de l'exécution, j'obtiens une erreur car Gamma
n'est pas définie, mais ce n'est pas le cas pour Delta
ou Alpha
? Que signifie const
ou que declare
signifient les déclarations ici?
Il y a aussi un preserveConstEnums
indicateur de compilateur - comment cela interagit-il avec ceux-ci?
enums
typescript
Ryan Cavanaugh
la source
la source
Réponses:
Il y a quatre aspects différents des énumérations dans TypeScript dont vous devez être conscient. Tout d'abord, quelques définitions:
"objet de recherche"
Si vous écrivez cette énumération:
TypeScript émettra l'objet suivant:
J'appellerai cela l'objet de recherche . Son objectif est double: servir de correspondance entre les chaînes et les nombres , par exemple lors de l'écriture
Foo.X
ouFoo['X']
, et servir de correspondance entre les nombres et les chaînes . Ce mappage inversé est utile à des fins de débogage ou de journalisation - vous aurez souvent la valeur0
ou1
et souhaitez obtenir la chaîne correspondante"X"
ou"Y"
."déclarer" ou " ambiant "
Dans TypeScript, vous pouvez «déclarer» des choses que le compilateur devrait connaître, mais pas réellement pour lesquelles émettre du code. Ceci est utile lorsque vous avez des bibliothèques comme jQuery qui définissent un objet (par exemple
$
) sur lequel vous voulez des informations de type, mais qui n'ont pas besoin de code créé par le compilateur. La spécification et d'autres documents se réfèrent aux déclarations faites de cette façon comme étant dans un contexte «ambiant»; il est important de noter que toutes les déclarations dans un.d.ts
fichier sont "ambiantes" (soit nécessitant undeclare
modificateur explicite, soit l'avoir implicitement, selon le type de déclaration)."inlining"
Pour des raisons de performances et de taille de code, il est souvent préférable de remplacer une référence à un membre enum par son équivalent numérique lors de la compilation:
La spécification appelle cette substitution , je l'appellerai inlining car elle sonne plus cool. Parfois, vous ne souhaiterez pas que les membres d'énumération soient insérés, par exemple parce que la valeur d'énumération pourrait changer dans une version future de l'API.
Enums, comment fonctionnent-ils?
Décomposons cela par chaque aspect d'une énumération. Malheureusement, chacune de ces quatre sections fera référence aux termes de toutes les autres, vous aurez donc probablement besoin de lire tout cela plus d'une fois.
calculée vs non calculée (constante)
Les membres d'énumération peuvent être calculés ou non. La spécification appelle les membres non calculés constants , mais je les appellerai non calculés pour éviter toute confusion avec const .
Un membre enum calculé est un membre dont la valeur n'est pas connue au moment de la compilation. Les références aux membres calculés ne peuvent pas être insérées, bien sûr. Inversement, un membre enum non calculé est une fois dont la valeur est connue au moment de la compilation. Les références aux membres non calculés sont toujours insérées.
Quels membres d'énumération sont calculés et lesquels ne sont pas calculés? Premièrement, tous les membres d'une
const
énumération sont constants (c'est-à-dire non calculés), comme son nom l'indique. Pour une énumération non-const, cela dépend si vous regardez une énumération ambiante (déclarée) ou une énumération non ambiante.Un membre de a
declare enum
(ie ambient enum) est constant si et seulement s'il a un initialiseur. Sinon, il est calculé. Notez que dans adeclare enum
, seuls les initialiseurs numériques sont autorisés. Exemple:Enfin, les membres des énumérations non déclarées non const sont toujours considérés comme calculés. Cependant, leurs expressions d'initialisation sont réduites à des constantes si elles sont calculables au moment de la compilation. Cela signifie que les membres d'énumération non const ne sont jamais incorporés (ce comportement a changé dans TypeScript 1.5, voir «Modifications dans TypeScript» en bas)
const vs non-const
const
Une déclaration enum peut avoir le
const
modificateur. Si une énumération estconst
, toutes les références à ses membres sont incorporées.Les énumérations const ne produisent pas d'objet de recherche lors de la compilation. Pour cette raison, il est erroné de faire référence
Foo
dans le code ci-dessus, sauf dans le cadre d'une référence de membre. AucunFoo
objet ne sera présent lors de l'exécution.non const
Si une déclaration d'énumération n'a pas le
const
modificateur, les références à ses membres sont insérées uniquement si le membre n'est pas calculé. Une énumération non const et non déclarée produira un objet de recherche.déclarer (ambiant) vs non-déclarer
Une préface importante est que
declare
dans TypeScript a une signification très spécifique: cet objet existe ailleurs . C'est pour décrire des objets existants . Utiliserdeclare
pour définir des objets qui n'existent pas réellement peut avoir de mauvaises conséquences; nous les explorerons plus tard.déclarer
A
declare enum
n'émettra pas d'objet de recherche. Les références à ses membres sont insérées si ces membres sont calculés (voir ci-dessus sur calculé vs non calculé).Il est important de noter que d' autres formes de référence à un
declare enum
sont autorisés, par exemple , ce code est pas une erreur de compilation mais va échouer à l' exécution:Cette erreur relève de la catégorie "Ne mentez pas au compilateur". Si vous n'avez pas d'objet nommé
Foo
au moment de l'exécution, n'écrivez pasdeclare enum Foo
!A
declare const enum
n'est pas différent de aconst enum
, sauf dans le cas de --preserveConstEnums (voir ci-dessous).non-déclarer
Une énumération non déclarée produit un objet de recherche si ce n'est pas le cas
const
. L'intégration est décrite ci-dessus.--preserveConstEnums indicateur
Cet indicateur a exactement un effet: les énumérations const non déclarées émettront un objet de recherche. L'intégration n'est pas affectée. Ceci est utile pour le débogage.
Erreurs courantes
L'erreur la plus courante est d'utiliser un
declare enum
quand un régulierenum
ouconst enum
serait plus approprié. Une forme courante est la suivante:Rappelez-vous la règle d'or: jamais des
declare
choses qui n'existent pas . À utiliserconst enum
si vous voulez toujours l'incrustation ouenum
si vous voulez l'objet de recherche.Changements dans TypeScript
Entre TypeScript 1.4 et 1.5, il y a eu un changement dans le comportement (voir https://github.com/Microsoft/TypeScript/issues/2183 ) pour que tous les membres d'énumérations non déclarées non const soient traités comme calculés, même si ils sont explicitement initialisés avec un littéral. Ce «débris le bébé», pour ainsi dire, rend le comportement en ligne plus prévisible et sépare plus proprement le concept de
const enum
régulierenum
. Avant ce changement, les membres non calculés des non-const enums étaient intégrés de manière plus agressive.la source
enum
types, merci!const
pour les types enum déclarés.Il se passe plusieurs choses ici. Allons au cas par cas.
énumération
Tout d'abord, une simple énumération ancienne. Une fois compilé en JavaScript, cela émettra une table de recherche.
La table de recherche ressemble à ceci:
Ensuite, lorsque vous avez
Cheese.Brie
dans TypeScript, il émetCheese.Brie
en JavaScript qui évalue à 0.Cheese[0]
émetCheese[0]
et évalue réellement à"Brie"
.const énumération
Aucun code n'est réellement émis pour cela! Ses valeurs sont intégrées. Les éléments suivants émettent la valeur 0 elle-même en JavaScript:
const enum
s 'inlining peut être utile pour des raisons de performances.Mais qu'en est-il
Bread[0]
? Cela provoquera une erreur au moment de l'exécution et votre compilateur devrait l'attraper. Il n'y a pas de table de recherche et le compilateur n'est pas en ligne ici.Notez que dans le cas ci-dessus, l'indicateur --preserveConstEnums provoquera l'émission d'une table de recherche par Bread. Cependant, ses valeurs seront toujours intégrées.
déclarer enum
Comme pour les autres utilisations de
declare
,declare
n'émet aucun code et s'attend à ce que vous ayez défini le code réel ailleurs. Cela n'émet aucune table de recherche:Wine.Red
émetWine.Red
en JavaScript, mais il n'y aura pas de table de recherche Wine à référencer, c'est donc une erreur à moins que vous ne l'ayez défini ailleurs.declare const enum
Cela n'émet aucune table de recherche:
Mais c'est en ligne!
Fruit.Apple
émet 0. Mais encore une fois, il yFruit[0]
aura une erreur lors de l'exécution car il n'est pas en ligne et il n'y a pas de table de recherche.J'ai écrit ceci dans ce terrain de jeu. Je recommande d'y jouer pour comprendre quel TypeScript émet quel JavaScript.
la source
Bread[0]
génère une erreur de compilation: «Un membre const enum n'est accessible qu'à l'aide d'une chaîne littérale.»