O TypeScript tem várias maneiras diferentes de definir um enum:
enum Alpha { X, Y, Z }
const enum Beta { X, Y, Z }
declare enum Gamma { X, Y, Z }
declare const enum Delta { X, Y, Z }
Se tento usar um valor Gamma
em tempo de execução, obtenho um erro porque Gamma
não está definido, mas não é o caso de Delta
ou Alpha
? O que significa const
ou declare
significa nas declarações aqui?
Há também um preserveConstEnums
sinalizador do compilador - como isso interage com eles?
enums
typescript
Ryan Cavanaugh
fonte
fonte
Respostas:
Existem quatro aspectos diferentes dos enums no TypeScript que você precisa conhecer. Primeiro, algumas definições:
"objeto de pesquisa"
Se você escrever este enum:
O TypeScript emitirá o seguinte objeto:
Vou me referir a isso como o objeto de pesquisa . Seu propósito é duplo: servir como um mapeamento de strings para números , por exemplo, ao escrever
Foo.X
ouFoo['X']
, e servir como um mapeamento de números para strings . Esse mapeamento reverso é útil para fins de depuração ou registro - você geralmente terá o valor0
ou1
e deseja obter a string correspondente"X"
ou"Y"
."declarar" ou " ambiente "
No TypeScript, você pode "declarar" coisas que o compilador deve saber, mas não para as quais realmente emitir código. Isso é útil quando você tem bibliotecas como jQuery que definem algum objeto (por exemplo
$
) sobre o qual deseja digitar informações, mas não precisa de nenhum código criado pelo compilador. A especificação e outras documentações referem-se a declarações feitas dessa maneira como sendo em um contexto "ambiente"; é importante observar que todas as declarações em um.d.ts
arquivo são "ambientais" (exigindo umdeclare
modificador explícito ou tendo-o implicitamente, dependendo do tipo de declaração)."inlining"
Por motivos de desempenho e tamanho do código, geralmente é preferível ter uma referência a um membro enum substituída por seu equivalente numérico quando compilado:
A especificação chama isso de substituição , vou chamá-lo inlining porque soa mais legal. Às vezes, você não vai querer que os membros do enum sejam sequenciais, por exemplo, porque o valor do enum pode mudar em uma versão futura da API.
Enums, como funcionam?
Vamos dividir isso por cada aspecto de um enum. Infelizmente, cada uma dessas quatro seções fará referência a termos de todas as outras, então você provavelmente precisará ler tudo isso mais de uma vez.
calculado vs não calculado (constante)
Os membros de Enum podem ser calculados ou não. A especificação chama constantes de membros não computados , mas vou chamá-los de não computados para evitar confusão com const .
Um membro enum calculado é aquele cujo valor não é conhecido em tempo de compilação. As referências a membros computados não podem ser sequenciais, é claro. Por outro lado, um membro enum não calculado é uma vez cujo valor é conhecido em tempo de compilação. As referências a membros não computados são sempre sequenciais.
Quais membros enum são computados e quais não são? Primeiro, todos os membros de um
const
enum são constantes (ou seja, não calculados), como o nome indica. Para um enum não constante, depende se você está olhando um enum ambiente (declarar) ou um enum não ambiente.Um membro de a
declare enum
(isto é, enum ambiente) é constante se e somente se tiver um inicializador. Caso contrário, é calculado. Observe que em adeclare enum
, apenas inicializadores numéricos são permitidos. Exemplo:Finalmente, os membros de enums não declarados não constantes são sempre considerados computados. No entanto, suas expressões de inicialização são reduzidas a constantes se forem computáveis em tempo de compilação. Isso significa que os membros enum não constantes nunca são embutidos (esse comportamento mudou no TypeScript 1.5, consulte "Alterações no TypeScript" na parte inferior)
const vs não const
const
Uma declaração enum pode ter o
const
modificador. Se um enum forconst
, todas as referências a seus membros serão incorporadas.enums const não produzem um objeto de pesquisa quando compilados. Por esse motivo, é um erro fazer referência
Foo
no código acima, exceto como parte de uma referência de membro. NenhumFoo
objeto estará presente no tempo de execução.não const
Se uma declaração enum não tiver o
const
modificador, as referências a seus membros serão sequenciadas apenas se o membro não for computado. Um enum não const e não declarado produzirá um objeto lookup.declarar (ambiente) vs não declarar
Um prefácio importante é que
declare
no TypeScript tem um significado muito específico: este objeto existe em outro lugar . É para descrever objetos existentes . Usardeclare
para definir objetos que não existem realmente pode ter consequências ruins; vamos explorar isso mais tarde.declarar
A
declare enum
não emitirá um objeto de pesquisa. As referências aos seus membros são sequenciais se esses membros forem computados (veja acima em computado vs não computado).É importante notar que as outras formas de referência a um
declare enum
são permitidos, por exemplo, este código é não um erro de compilação, mas irá falhar em tempo de execução:Este erro se enquadra na categoria de "Não minta para o compilador". Se você não tiver um objeto nomeado
Foo
em tempo de execução, não escrevadeclare enum Foo
!A
declare const enum
não é diferente de aconst enum
, exceto no caso de --preserveConstEnums (veja abaixo).não declarar
Um enum não declarado produz um objeto de pesquisa se não for
const
. Inlining é descrito acima.--preserveConstEnums sinalizador
Este sinalizador tem exatamente um efeito: enums const não declarados emitirá um objeto de pesquisa. Inlining não é afetado. Isso é útil para depuração.
Erros comuns
O erro mais comum é usar um
declare enum
quando um normalenum
ouconst enum
seria mais apropriado. Uma forma comum é esta:Lembre-se da regra de ouro: nunca
declare
coisas que não existam de verdade . Useconst enum
se quiser sempre inlining ouenum
se quiser o objeto de pesquisa.Mudanças no TypeScript
Entre TypeScript 1.4 e 1.5, houve uma mudança no comportamento (consulte https://github.com/Microsoft/TypeScript/issues/2183 ) para fazer com que todos os membros de enums não declarados não constantes fossem tratados como calculados, mesmo que eles são inicializados explicitamente com um literal. Isso "desfaz o bebê", por assim dizer, tornando o comportamento inlining mais previsível e separando de forma mais clara o conceito de
const enum
regularenum
. Antes dessa mudança, os membros não computados de enums não constantes eram embutidos de forma mais agressiva.fonte
enum
tipos, obrigado!const
para os tipos enum declarados.Existem algumas coisas acontecendo aqui. Vamos caso a caso.
enum
Primeiro, um enum simples e antigo. Quando compilado para JavaScript, ele emitirá uma tabela de pesquisa.
A tabela de pesquisa é semelhante a esta:
Então, quando você tem o
Cheese.Brie
TypeScript, ele emiteCheese.Brie
em JavaScript que é avaliado como 0.Cheese[0]
emiteCheese[0]
e realmente avalia como"Brie"
.const enum
Nenhum código é realmente emitido para isso! Seus valores estão embutidos. O seguinte emite o próprio valor 0 em JavaScript:
const enum
s 'inlining pode ser útil por motivos de desempenho.Mas e quanto
Bread[0]
? Isso ocorrerá em tempo de execução e seu compilador deve detectá-lo. Não há tabela de pesquisa e o compilador não embutido aqui.Observe que, no caso acima, o sinalizador --preserveConstEnums fará com que o Bread emita uma tabela de pesquisa. Seus valores ainda estarão embutidos.
declarar enum
Tal como acontece com outros usos de
declare
,declare
não emite código e espera que você tenha definido o código real em outro lugar. Isso não emite uma tabela de pesquisa:Wine.Red
emiteWine.Red
em JavaScript, mas não haverá nenhuma tabela de pesquisa Wine para referência, então é um erro, a menos que você tenha definido em outro lugar.declarar const enum
Isso não emite uma tabela de pesquisa:
Mas faz inline!
Fruit.Apple
emite 0. Mas, novamente,Fruit[0]
apresentará um erro no tempo de execução porque não é embutido e não há tabela de pesquisa.Eu escrevi isso neste playground. Recomendo jogar lá para entender qual TypeScript emite qual JavaScript.
fonte
Bread[0]
lança um erro do compilador: “Um membro const enum só pode ser acessado usando um literal de string.”