Como converter uma sequência de caracteres em enumeração no TypeScript?


312

Eu defini a seguinte enumeração no TypeScript:

enum Color{
    Red, Green
}

Agora, na minha função, recebo cores como uma sequência. Eu tentei o seguinte código:

var green= "Green";
var color : Color = <Color>green; // Error: can't convert string to enum

Como posso converter esse valor em uma enumeração?

Respostas:


431

As enums no TypeScript 0.9 são baseadas em string + número. Você não deve precisar de asserção de tipo para conversões simples:

enum Color{
    Red, Green
}

// To String
 var green: string = Color[Color.Green];

// To Enum / number
var color : Color = Color[green];

Experimente online

Tenho documentação sobre esse e outros padrões do Enum no meu livro OSS: https://basarat.gitbook.io/typescript/type-system/enums


112
Isso não funciona com --noImplicitAny(no VS desmarcado "Permitir tipos implícitos 'qualquer'"). Produz error TS7017: Index signature of object type implicitly has an 'any' type.para mim isso funcionou: var color: Color = (<any>Color)[green];(testado com a versão 1.4)
Vojta

3
@Vojta disse certo. Não está funcionando no VS 2012. Este funcionou, mas var color: Color = (<any> Color) [green];
Faisal Mq

3
Também não funciona aqui, a documentação oficial parece confirmar que: typescriptlang.org/docs/handbook/release-notes/…
Pieter De Bie

26
Certifique-se de usá-lo se --noImplicitAny var color : Color = Color[green as keyof typeof Color];
Jonas #

2
@ Naxos84 Veja minhas respostas stackoverflow.com/a/56076148/294242
Jonas

123

A partir do Typecript 2.1, as chaves de seqüência de caracteres nas enumerações são fortemente digitadas. keyof typeofé usado para obter informações sobre as chaves de string disponíveis ( 1 ):

enum Color{
    Red, Green
}

let typedColor: Color = Color.Green;
let typedColorString: keyof typeof Color = "Green";

// Error "Black is not assignable ..." (indexing using Color["Black"] will return undefined runtime)
typedColorString = "Black";

// Error "Type 'string' is not assignable ..." (indexing works runtime)
let letColorString = "Red";
typedColorString = letColorString;

// Works fine
typedColorString = "Red";

// Works fine
const constColorString = "Red";
typedColorString = constColorString

// Works fine (thanks @SergeyT)
let letColorString = "Red";
typedColorString = letColorString as keyof typeof Color;

typedColor = Color[typedColorString];

https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types


4
Para que possamos usar o typecast::let s = "Green"; let typedColor = <keyof typeof Color> s;
SergeyT 8/17/17

Sim, e substituir letpor constfuncionará sem vazamento. Exemplo atualizado para esclarecer isso. Obrigado @SergeyT
Victor

1
typedColorString = Color["Black"];agora retornaerror TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'
Dominik

2
Resposta de uma linha:const color: Color = Color[colorString as keyof typeof Color];
cscan 6/11/19

38

Se você tem certeza de que uma sequência de entrada tem uma correspondência exata com a cor enum, use:

const color: Color = (<any>Color)["Red"];

No caso em que uma sequência de entrada pode não corresponder ao Enum, use:

const mayBeColor: Color | undefined = (<any>Color)["WrongInput"];
if (mayBeColor !== undefined){
     // TypeScript will understand that mayBeColor is of type Color here
}

Parque infantil


Se não convertermos enumpara <any>digitar, o TypeScript mostrará o erro:

O elemento tem implicitamente 'qualquer' tipo porque a expressão de índice não é do tipo 'número'.

Isso significa que, por padrão, o tipo TypeScript Enum funciona com índices numéricos, ou seja let c = Color[0], mas não com índices de string como let c = Color["string"]. Essa é uma restrição conhecida pela equipe da Microsoft para os índices de sequência de objetos de problemas mais gerais .


Você também pode converter para <número do tipo de cor>. Também "0" é a entrada errada também, mas não retornará indefinido, de modo a verificar typeof 'número' mayBeColor ===
Quentin 2

@ Quentin2 que tal uma string numérica? ou seja, typeof '0'deveria serstring
Patrick Michaelsen

36
enum Color{
    Red, Green
}

// To String
 var green: string = Color[Color.Green];

// To Enum / number
var color : Color = Color[green as keyof typeof Color]; //Works with --noImplicitAny

Este exemplo funciona com --noImplicitAnyno TypeScript

Fontes:
https://github.com/Microsoft/TypeScript/issues/13775#issuecomment-276381229 https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types


Eu não sei por que, mas esta solução não funciona em um const enum (usando o Typecript 3.8.3)
Robin-Hoodie

30

Esta nota se refere à resposta do basarat , não à pergunta original.

Eu tive um problema estranho em meu próprio projeto, em que o compilador estava dando um erro aproximadamente equivalente a "não é possível converter a seqüência de caracteres em cores" usando o equivalente deste código:

var colorId = myOtherObject.colorId; // value "Green";
var color: Color = <Color>Color[colorId]; // TSC error here: Cannot convert string to Color.

Descobri que a inferência do tipo de compilador estava ficando confusa e achava que colorIdera um valor enum e não um ID. Para corrigir o problema, tive que converter o ID como uma string:

var colorId = <string>myOtherObject.colorId; // Force string value here
var color: Color = Color[colorId]; // Fixes lookup here.

Não sei ao certo o que causou o problema, mas deixarei esta nota aqui, caso alguém tenha o mesmo problema que eu.


Obrigado! Esta é uma questão bastante tola e difícil de descobrir qual é o problema. Talvez o Typescript deva considerar uma maneira melhor de lidar com enums.
ChickenFeet 31/01

25

Eu consegui trabalhar usando o seguinte código.

var green= "Green";
var color : Color= <Color>Color[green];

23

Se você fornecer valores de sequência para sua enumeração, uma conversão direta funcionará perfeitamente.

enum Color {
  Green = "Green",
  Red = "Red"
}

const color = "Green";
const colorEnum = color as Color;

1
Muito simples. Agradável!
Bernoulli IT

1
Isso pode ser enganador, pois não protege contra cores inválidas. const colorEnum = "Blue" as Colornão irá errar, e você ficaria pensando que colorEnumestá tudo bem. Mas se você console.logfizesse isso, veria "Blue". A resposta de Artru é boa, porque colorEnumserá undefined- e você pode verificar isso especificamente.
M Falanga

20

Como você usa o texto datilografado: Muitas das soluções acima podem não funcionar ou são muito complexas.

Situação : as strings não são iguais aos valores da enumeração (a caixa difere)

enum Color {
  Green = "green",
  Red = "red"
}

Apenas use:

const color = "green" as Color

15

Também encontrei o mesmo erro do compilador. Apenas uma ligeira variação mais curta da abordagem de Sly_cardinal.

var color: Color = Color[<string>colorId];

Como uma adição: caso você tenha uma enumeração datilografada preenchida por uma camada javascript que serializou a enum como string (digamos, por exemplo, Asp Web API via AngularJS), você pode fazer myProp.color = Color[<string><any>myProp.color] Cheers
Victor

1
Essa deve ser a resposta reconhecida.
Miroslav Popov

9

Se o compilador TypeScript souber que o tipo de variável é string, isso funcionará:

let colorName : string = "Green";
let color : Color = Color[colorName];

Caso contrário, você deve convertê-lo explicitamente em uma string (para evitar avisos do compilador):

let colorName : any = "Green";
let color : Color = Color["" + colorName];

Em tempo de execução, ambas as soluções funcionarão.


3
por que não usar tipecast em <string>colorNamevez de "" + colorName?
precisa saber é o seguinte

7

Há muitas informações mistas nesta questão, então vamos abordar toda a implementação do TypeScript 2.x + no Guia do Nick para usar enums em modelos com TypeScript .

Este guia é para: pessoas que estão criando código do lado do cliente que está ingerindo um conjunto de seqüências conhecidas do servidor que seriam convenientemente modeladas como um Enum no lado do cliente.

Definir a enumeração

Vamos começar com o enum. Deve ser algo como isto:

export enum IssueType {
  REPS = 'REPS',
  FETCH = 'FETCH',
  ACTION = 'ACTION',
  UNKNOWN = 'UNKNOWN',
}

Duas coisas de nota aqui:

  1. Estamos declarando-os explicitamente como casos de enumeração com suporte a strings, o que nos permite instancia-los com strings, e não outros números não relacionados.

  2. Nós adicionamos uma opção que pode ou não existir em nosso modelo de servidor: UNKNOWN. Isso pode ser tratado como undefinedse você preferir, mas eu gosto de evitar | undefinedtipos sempre que possível para simplificar o manuseio.

A grande vantagem de ter um UNKNOWNcaso é que você pode ser realmente óbvio no código e criar estilos para casos de enum desconhecidos brilhantes em vermelho e intermitente, para que você saiba que não está lidando com algo corretamente.

Analisar a enumeração

Você pode estar usando esse enum incorporado em outro modelo, ou sozinho, mas precisará analisar o enum do tipo JSON ou XML (ha) da string em seu equivalente fortemente tipado. Quando incorporado em outro modelo, esse analisador vive no construtor de classe.

parseIssueType(typeString: string): IssueType {
  const type = IssueType[typeString];
  if (type === undefined) {
    return IssueType.UNKNOWN;
  }

  return type;
}

Se o enum for analisado corretamente, ele terminará como o tipo apropriado. Caso contrário, será undefinede você poderá interceptá-lo e devolver seu UNKNOWNcaso. Se você preferir usar undefinedcomo caso desconhecido, basta retornar qualquer resultado da tentativa de análise de enum.

A partir daí, é apenas uma questão de usar a função de análise e usar sua variável de tipo recém-forte.

const strongIssueType: IssueType = parseIssueType('ACTION');
// IssueType.ACTION
const wrongIssueType: IssueType = parseIssueType('UNEXPECTED');
// IssueType.UNKNOWN

6
Infelizmente, isso parece não estar correto ou, pelo menos, não generalizável. Funciona porque suas chaves são iguais às seqüências atribuídas. Se eles, como no meu caso, diferem, no entanto, isso não funciona. Nas palavras da documentação : "Lembre-se de que os membros da enum de sequência não recebem um mapeamento reverso". Seu código será compilado para algo como IssueType["REPS"]="REPS". Se você tivesse definido seu enum um pouco diferente, digamos, REPS="reps"isso renderia o IssueType["REPS"]="reps"que ...
altocumulus

... sempre retorne IssueType.UNKNOWNporque não há chave repsna sua enumeração. Pena que ainda não encontrei solução para isso, pois minhas cordas contêm hífens, o que as torna inutilizáveis ​​como chaves.
Altocumulus

Finalmente, encontrei uma solução nesta resposta convencendo o compilador de que não se tratava de um enum de string. Pode valer a pena editar essas informações em sua própria resposta.
Altocumulus

7

Eu estava procurando por uma resposta que pudesse obter um enumde a string, mas no meu caso, os valores de enumerações tinham valores de sequência diferentes. O OP tinha um enum simples para Color, mas eu tinha algo diferente:

enum Gender {
  Male = 'Male',
  Female = 'Female',
  Other = 'Other',
  CantTell = "Can't tell"
}

Quando você tenta resolver Gender.CantTellcom uma "Can't tell"sequência, ela retorna undefinedcom a resposta original.

Outra resposta

Basicamente, eu vim com outra resposta, fortemente inspirada por esta resposta :

export const stringToEnumValue = <ET, T>(enumObj: ET, str: string): T =>
  (enumObj as any)[Object.keys(enumObj).filter(k => (enumObj as any)[k] === str)[0]];

Notas

  • Tomamos o primeiro resultado de filter, supondo que o cliente esteja passando uma string válida do enum. Se não for o caso, undefinedserá devolvido.
  • Nós fundido enumObjpara any, porque com texto dactilografado 3.0+ (actualmente a utilizar texto dactilografado 3,5), a enumObjé resolvido como unknown.

Exemplo de Uso

const cantTellStr = "Can't tell";

const cantTellEnumValue = stringToEnumValue<typeof Gender, Gender>(Gender, cantTellStr);
console.log(cantTellEnumValue); // Can't tell

Nota: E, como alguém apontou em um comentário, eu também queria usar o noImplicitAny.

Versão atualizada

Sem elenco anye tipografia adequada.

export const stringToEnumValue = <T, K extends keyof T>(enumObj: T, value: string): T[keyof T] | undefined =>
  enumObj[Object.keys(enumObj).filter((k) => enumObj[k as K].toString() === value)[0] as keyof typeof enumObj];

Além disso, a versão atualizada possui uma maneira mais fácil de chamá-la e é mais legível:

stringToEnumValue(Gender, "Can't tell");

6

Eu precisava saber como fazer um loop sobre valores de enumeração (estava testando muitas permutações de várias enumerações) e achei que funcionava bem:

export enum Environment {
    Prod = "http://asdf.com",
    Stage = "http://asdf1234.com",
    Test = "http://asdfasdf.example.com"
}

Object.keys(Environment).forEach((environmentKeyValue) => {
    const env = Environment[environmentKeyValue as keyof typeof Environment]
    // env is now equivalent to Environment.Prod, Environment.Stage, or Environment.Test
}

Fonte: https://blog.mikeski.net/development/javascript/typescript-enums-to-from-string/


Esta resposta é genial! Adoro. Especialmente a maneira como você cria um enum da string. Isso pode poupar bastante digitação ao testar enumerações ou outros casos.
Florian Leitgeb

Sim, eu uso isso com Jest de eachtestar todos os casos enum único com apenas um método
mikeb

3

Enum

enum MyEnum {
    First,
    Second,
    Three
}

Uso da amostra

const parsed = Parser.parseEnum('FiRsT', MyEnum);
// parsed = MyEnum.First 

const parsedInvalid= Parser.parseEnum('other', MyEnum);
// parsedInvalid = undefined

Ignorar análise sensível a maiúsculas e minúsculas

class Parser {
    public static parseEnum<T>(value: string, enumType: T): T[keyof T] | undefined {
        if (!value) {
            return undefined;
        }

        for (const property in enumType) {
            const enumMember = enumType[property];
            if (typeof enumMember === 'string') {
                if (enumMember.toUpperCase() === value.toUpperCase()) {
                    const key = enumMember as string as keyof typeof enumType;
                    return enumType[key];
                }
            }
        }
        return undefined;
    }
}

Qualquer pessoa que tenha enum como eu deveria colocar return enumType[property];em um caso quando seus enum O artigo olha comoSkills = "anyvalue"
neustart47

@ neustart47 você poderia fazer a pergunta?
Очир Дармаев

não é uma pergunta. Acabei de mencionar algumas alterações para quem está procurando o mesmo caso que eu. Sua resposta está correta.
neustart47

2

As enums criadas da maneira que você fez são compiladas em um objeto que armazena os mapeamentos para frente (name -> value)e para trás (value -> name). Como podemos observar nesta captura de tela do chrome devtools:

insira a descrição da imagem aqui

Aqui está um exemplo de como o mapeamento duplo funciona e como converter de um para outro:

enum Color{
    Red, Green
}
// To Number
var greenNr: number = Color['Green'];
console.log(greenNr); // logs 1

// To String
var greenString: string = Color[Color['Green']];  // or Color[Color[1]
console.log(greenString); // logs Green

// In your example

// recieve as Color.green instead of the string green
var green: string = Color[Color.Green];  

// obtain the enum number value which corresponds to the Color.green property
var color: Color = (<any>Color)[green];  

console.log(color); // logs 1

1

Tente isto

var color: Color = (qualquer cor) ["Verde];

Isso funciona bem para a versão 3.5.3


0

Se você estiver usando espaços para nome para estender a funcionalidade do seu enum, também poderá fazer algo como

    enum Color {
        Red, Green
    }

    export namespace Color {
      export function getInstance(color: string) : Color {
        if(color == 'Red') {
          return Color.Red;
        } else if (color == 'Green') {
          return Color.Green;
        }
      }
    }

e use assim

  Color.getInstance('Red');

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.