Comment convertir une chaîne en énumération en TypeScript?

312

J'ai défini l'énumération suivante dans TypeScript:

enum Color{
    Red, Green
}

Maintenant, dans ma fonction, je reçois la couleur sous forme de chaîne. J'ai essayé le code suivant:

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

Comment puis-je convertir cette valeur en une énumération?

Amitabh
la source

Réponses:

431

Les énumérations dans TypeScript 0.9 sont basées sur une chaîne + un nombre. Vous ne devriez pas avoir besoin d'une assertion de type pour les conversions simples:

enum Color{
    Red, Green
}

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

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

Essayez-le en ligne

J'ai une documentation à ce sujet et sur d'autres modèles Enum dans mon livre OSS: https://basarat.gitbook.io/typescript/type-system/enums

basarat
la source
112
Cela ne fonctionne pas avec --noImplicitAny(dans VS non coché "Autoriser les types 'any' implicites"). Il produit error TS7017: Index signature of object type implicitly has an 'any' type.Pour moi, cela a fonctionné: var color: Color = (<any>Color)[green];(testé avec la version 1.4)
Vojta
3
@Vojta a dit à droite. Son ne fonctionne pas dans VS 2012. Celui-ci a fonctionné mais var couleur: Color = (<any> Color) [vert];
Faisal Mq
3
Cela ne fonctionne pas non plus, la documentation officielle semble confirmer que: typescriptlang.org/docs/handbook/release-notes/…
Pieter De Bie
26
Assurez-vous de l'utiliser si --noImplicitAny var color : Color = Color[green as keyof typeof Color];
Jonas
2
@ Naxos84 Voir mon réponse stackoverflow.com/a/56076148/294242
Jonas
123

À partir de Typescript 2.1, les clés de chaîne dans les énumérations sont fortement typées. keyof typeofest utilisé pour obtenir des informations sur les clés de chaîne disponibles ( 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

Victor
la source
4
Nous pouvons donc utiliser typecast:let s = "Green"; let typedColor = <keyof typeof Color> s;
SergeyT
Oui, et le remplacer letpar constfonctionnera sans lancer. Exemple mis à jour pour clarifier cela. Merci @SergeyT
Victor
1
typedColorString = Color["Black"];revient maintenanterror TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'
Dominik
2
Une réponse en une seule ligne:const color: Color = Color[colorString as keyof typeof Color];
cscan
38

Si vous êtes sûr qu'une chaîne d'entrée a une correspondance exacte avec Color enum, utilisez:

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

Dans le cas où une chaîne d'entrée peut ne pas correspondre à Enum, utilisez:

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

Terrain de jeux


Si nous ne la chassez enumà <any>taper alors tapuscrit affiche l'erreur:

L'élément a implicitement un type 'any' car l'expression d'index n'est pas de type 'number'.

Cela signifie que par défaut, le type Enum TypeScript fonctionne avec les index numériques, c'est let c = Color[0]-à- dire , mais pas avec les index de chaîne comme let c = Color["string"]. Il s'agit d'une restriction connue de l'équipe Microsoft pour le problème plus général des index de chaînes d'objets .

Artru
la source
Vous pouvez également convertir en <clé de type de couleur>. "0" est également une mauvaise entrée mais ne retournera pas indéfini, alors vérifiez le type de mayBeColor === 'nombre'
Quentin 2
@ Quentin2 qu'en est-il d'une chaîne numérique? c'est-à-dire typeof '0'devrait êtrestring
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

Cet exemple fonctionne avec --noImplicitAnydans TypeScript

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

Jonas
la source
Je ne sais pas pourquoi, mais cette solution ne fonctionne pas sur une énumération constante (en utilisant Typescript 3.8.3)
Robin-Hoodie
30

Cette note se rapporte à la réponse de Basarat , pas la question d'origine.

J'ai eu un problème étrange dans mon propre projet où le compilateur donnait une erreur à peu près équivalente à "ne peut pas convertir la chaîne en couleur" en utilisant l'équivalent de ce code:

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

J'ai trouvé que l'inférence du type de compilateur devenait confuse et il pensait que colorIdc'était une valeur d'énumération et non un ID. Pour résoudre le problème, j'ai dû convertir l'ID en chaîne:

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

Je ne sais pas ce qui a causé le problème, mais je vais laisser cette note ici au cas où quelqu'un rencontrerait le même problème que moi.

Sly_cardinal
la source
Je vous remercie! C'est un problème assez stupide et difficile à comprendre quel est le problème.Peut-être que Typescript devrait envisager de trouver une meilleure façon de gérer les énumérations.
ChickenFeet
25

Je l'ai fait fonctionner en utilisant le code suivant.

var green= "Green";
var color : Color= <Color>Color[green];
Amitabh
la source
23

Si vous fournissez des valeurs de chaîne à votre énumération, une distribution droite fonctionne très bien.

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

const color = "Green";
const colorEnum = color as Color;
alexanie
la source
1
Très simple. Agréable!
Bernoulli IT
1
Cela pourrait être trompeur car il ne protège pas contre les couleurs invalides. const colorEnum = "Blue" as Colorne fera pas d'erreur, et vous seriez laissé penser que colorEnumc'est OK. Mais si vous y étiez console.log, vous verriez "Blue". La réponse d'Artru est agréable, car colorEnumelle le sera undefined- et vous pourrez alors le vérifier spécifiquement.
M Falanga
20

Étant donné que vous utilisez tapuscrit: la plupart des solutions ci-dessus peuvent ne pas fonctionner ou sont trop complexes.

Situation : les chaînes ne sont pas les mêmes que les valeurs d'énumération (le boîtier diffère)

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

Utilisez simplement:

const color = "green" as Color
Nick N.
la source
15

J'ai également rencontré la même erreur de compilation. Juste une légère variation plus courte de l'approche de Sly_cardinal.

var color: Color = Color[<string>colorId];
Chris
la source
En complément: dans le cas où vous avez une énumération dactylographiée remplie par une couche javascript qui a sérialisé l'énumération en chaîne (par exemple, l'API Web Asp via AngularJS), vous pouvez faire myProp.color = Color[<string><any>myProp.color] Cheers
Victor
1
Ce doit être la réponse reconnue.
Miroslav Popov
9

Si le compilateur TypeScript sait que le type de variable est une chaîne, cela fonctionne:

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

Sinon, vous devez le convertir explicitement en chaîne (pour éviter les avertissements du compilateur):

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

Au moment de l'exécution, les deux solutions fonctionnent.

Luka
la source
3
pourquoi ne pas simplement utiliser typecast <string>colorNameau lieu de "" + colorName?
SergeyT
7

Il y a beaucoup d'informations mitigées dans cette question, nous allons donc couvrir toute l'implémentation de TypeScript 2.x + dans le Guide de Nick pour utiliser les énumérations dans les modèles avec TypeScript .

Ce guide est destiné aux personnes qui créent du code côté client qui ingère un ensemble de chaînes connues du serveur qui serait commodément modélisé en tant qu'énumération côté client.

Définir l'énumération

Commençons par l'énumération. Ça devrait ressembler a quelque chose comme ca:

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

Deux choses à noter ici:

  1. Nous les déclarons explicitement comme des cas d'énumération basés sur des chaînes, ce qui nous permet de les instancier avec des chaînes, et non avec d'autres nombres non liés.

  2. Nous avons ajouté une option qui peut ou peut ne pas exister sur notre modèle de serveur: UNKNOWN. Cela peut être géré comme undefinedsi vous préférez, mais j'aime éviter | undefinedautant que possible les types pour simplifier la manipulation.

La grande chose d'avoir un UNKNOWNcas est que vous pouvez être vraiment évident à ce sujet dans le code et créer des styles pour les cas d'énumération inconnus rouge vif et clignotant afin que vous sachiez que vous ne gérez pas quelque chose correctement.

Analyser l'énumération

Vous pouvez utiliser cette énumération incorporée dans un autre modèle, ou tout seul, mais vous devrez analyser l'énumération de type y à partir de JSON ou XML (ha) dans votre homologue fortement typé. Lorsqu'il est intégré dans un autre modèle, cet analyseur vit dans le constructeur de classe.

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

  return type;
}

Si l'énumération est correctement analysée, elle deviendra le type approprié. Sinon, ce sera le cas undefinedet vous pourrez l'intercepter et retourner votre UNKNOWNcas. Si vous préférez utiliser undefinedcomme cas inconnu, vous pouvez simplement renvoyer n'importe quel résultat de l'analyse enum tentée.

À partir de là, il suffit d'utiliser la fonction d'analyse et d'utiliser votre nouvelle variable typée forte.

const strongIssueType: IssueType = parseIssueType('ACTION');
// IssueType.ACTION
const wrongIssueType: IssueType = parseIssueType('UNEXPECTED');
// IssueType.UNKNOWN
pseudo
la source
6
Malheureusement, cela ne semble pas correct ou, du moins, n'est pas généralisable. Cela fonctionne parce que vos clés sont égales aux chaînes qui leur ont été attribuées. S'ils diffèrent, comme dans mon cas, cependant, cela ne fonctionne pas. Selon les termes de la documentation : "Gardez à l'esprit que les membres énumération de chaîne n'obtiennent pas du tout de mappage inverse." Votre code se compilera en quelque chose comme IssueType["REPS"]="REPS". Si vous aviez défini votre enum un peu différent, disons, REPS="reps"cela donnerait IssueType["REPS"]="reps"ce qui ...
altocumulus
... revenez toujours IssueType.UNKNOWNcar il n'y a pas de clé repsdans votre énumération. Dommage, je n'ai toujours trouvé aucune solution de travail pour cela car mes chaînes contiennent des tirets ce qui les rend inutilisables comme clés.
altocumulus
Enfin, j'ai trouvé une solution dans cette réponse en convaincant le compilateur qu'il ne s'agissait pas d'une énumération de chaînes. Il pourrait être utile de modifier ces informations dans votre propre réponse.
altocumulus
7

Je cherchais une réponse qui peut obtenir un enumde a string, mais dans mon cas, les valeurs enums avaient des valeurs de chaîne différentes. L'OP avait une énumération simple Color, mais j'avais quelque chose de différent:

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

Lorsque vous essayez de résoudre Gender.CantTellavec une "Can't tell"chaîne, il revient undefinedavec la réponse d'origine.

Une autre réponse

Fondamentalement, j'ai trouvé une autre réponse, fortement inspirée par cette réponse :

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

Remarques

  • Nous prenons le premier résultat de filter, en supposant que le client passe une chaîne valide à partir de l'énumération. Si ce n'est pas le cas, undefinedsera retourné.
  • Nous convertissons enumObjen any, car avec TypeScript 3.0+ (utilisant actuellement TypeScript 3.5), le enumObjest résolu comme unknown.

Exemple d'utilisation

const cantTellStr = "Can't tell";

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

Remarque: Et, comme quelqu'un l'a souligné dans un commentaire, je voulais également utiliser le noImplicitAny.

Version mise à jour

Pas de anytranstypage et typages appropriés.

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];

De plus, la version mise à jour a un moyen plus simple de l'appeler et est plus lisible:

stringToEnumValue(Gender, "Can't tell");
elbaid
la source
6

J'avais besoin de savoir comment boucler sur les valeurs d'énumération (testait beaucoup de permutations de plusieurs énumérations) et j'ai trouvé que cela fonctionnait bien:

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
}

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

mikeb
la source
Cette réponse est géniale! Aimer. Surtout la façon dont vous faites une énumération de la chaîne. Cela peut vous faire économiser beaucoup de frappe lors du test d'énumérations ou d'autres cas.
Florian Leitgeb
Oui, je l'utilise avec Jest's eachpour tester chaque cas d'énumération unique avec une seule méthode
mikeb
3

Enum

enum MyEnum {
    First,
    Second,
    Three
}

Exemple d'utilisation

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

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

Ignorer l'analyse sensible à la casse

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;
    }
}
Очир Дармаев
la source
Quiconque a une énumération comme moi devrait mettre return enumType[property];dans un étui lorsque votre élément d'énumération ressemble àSkills = "anyvalue"
neustart47
@ neustart47 pourriez-vous s'il vous plaît poser la question?
Очир Дармаев
ce n'est pas une question. Je viens de mentionner quelques changements pour quiconque cherche le même cas que moi. Votre réponse est correcte.
neustart47
2

Les énumérations créées comme vous l'avez fait sont compilées dans un objet qui stocke les mappages avant (name -> value)et arrière (value -> name). Comme nous pouvons le constater sur cette capture d'écran de chrome devtools:

entrez la description de l'image ici

Voici un exemple du fonctionnement du mappage double et de la conversion de l'un à l'autre:

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
Willem van der Veen
la source
1

Essaye ça

var color: Color = (Color as any) ["Vert];

Cela fonctionne bien pour la version 3.5.3

Oyeme
la source
0

Si vous utilisez des espaces de noms pour étendre les fonctionnalités de votre énumération, vous pouvez également faire quelque chose comme

    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;
        }
      }
    }

et l'utiliser comme ça

  Color.getInstance('Red');
andrei.b
la source
0

une autre variation peut être

const green= "Green";

const color : Color = Color[green] as Color;
Anuranjan Srivastav
la source