Bagaimana cara mengonversi string menjadi enum dalam TypeScript?

312

Saya telah mendefinisikan enum berikut dalam TypeScript:

enum Color{
    Red, Green
}

Sekarang dalam fungsi saya, saya menerima warna sebagai string. Saya sudah mencoba kode berikut:

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

Bagaimana saya bisa mengubah nilai itu menjadi enum?

Amitabh
sumber

Jawaban:

431

Enums di TypeScript 0.9 berbasis string + angka. Anda tidak perlu mengetikkan pernyataan untuk konversi sederhana:

enum Color{
    Red, Green
}

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

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

Cobalah online

Saya memiliki dokumentasi tentang hal ini dan pola Enum lainnya dalam buku OSS saya: https://basarat.gitbook.io/typescript/type-system/enums

basarat
sumber
112
Ini tidak bekerja dengan --noImplicitAny(dalam VS tidak dicentang "Izinkan jenis 'apa pun' tersirat"). Ini menghasilkan error TS7017: Index signature of object type implicitly has an 'any' type.Bagi saya ini bekerja: var color: Color = (<any>Color)[green];(diuji dengan versi 1.4)
Vojta
3
@ Vojta berkata benar. Ini tidak berfungsi di VS 2012. Yang ini berfungsi tetapi var color: Color = (<any> Color) [green];
Faisal Mq
3
Juga tidak berfungsi di sini, dokumentasi resmi tampaknya mengkonfirmasi bahwa: typescriptlang.org/docs/handbook/release-notes/…
Pieter De Bie
26
Pastikan untuk menggunakan ini jika --noImplicitAny var color : Color = Color[green as keyof typeof Color];
Jonas
2
@ Naxos84 Lihat jawabanku stackoverflow.com/a/56076148/294242
Jonas
123

Pada Keyscript 2.1 kunci string dalam enums sangat diketik. keyof typeofdigunakan untuk mendapatkan info tentang kunci string yang tersedia ( 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

Pemenang
sumber
4
Jadi kita bisa menggunakan typecast:let s = "Green"; let typedColor = <keyof typeof Color> s;
SergeyT
Yap, dan mengganti letdengan constakan bekerja tanpa casting. Contoh yang diperbarui untuk memperjelas ini. Terima kasih @SergeyT
Victor
1
typedColorString = Color["Black"];sekarang kembalierror TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'
Dominik
2
Jawaban satu baris:const color: Color = Color[colorString as keyof typeof Color];
cscan
38

Jika Anda yakin string input memiliki kecocokan persis dengan Color enum, gunakan:

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

Dalam hal string input mungkin tidak cocok dengan Enum, gunakan:

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

Tempat bermain


Jika kita tidak melakukan cast enumuntuk <any>mengetik maka TypeScript akan menampilkan kesalahan:

Elemen secara implisit memiliki tipe 'apa saja' karena ekspresi indeks bukan tipe 'angka'.

Ini berarti bahwa secara default tipe TypeScript Enum bekerja dengan indeks angka, yaitu let c = Color[0], tetapi tidak dengan indeks string seperti let c = Color["string"]. Ini adalah batasan yang diketahui oleh tim Microsoft untuk masalah indeks string objek yang lebih umum .

Artru
sumber
Anda juga dapat melakukan cast ke <keyof typeof Color>. Juga "0" adalah input yang salah juga tetapi tidak akan kembali tidak terdefinisi, jadi periksa typeof mayBeColor === 'number'
Quentin 2
@ Quentin2 bagaimana dengan string numerik? yaitu typeof '0'seharusnyastring
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

Contoh ini berfungsi dengan --noImplicitAnydi TypeScript

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

Jonas
sumber
Saya tidak tahu mengapa, tetapi solusi ini tidak bekerja pada const enum (menggunakan Script 3.8.3)
Robin-Hoodie
30

Catatan ini terkait dengan jawaban basarat , bukan pertanyaan aslinya.

Saya punya masalah aneh di proyek saya sendiri di mana kompiler memberikan kesalahan kira-kira setara dengan "tidak dapat mengubah string ke Warna" menggunakan setara dengan kode ini:

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

Saya menemukan bahwa menyimpulkan tipe kompiler semakin membingungkan dan berpikir bahwa colorIditu adalah nilai enum dan bukan ID. Untuk memperbaiki masalah saya harus melemparkan ID sebagai string:

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

Saya tidak yakin apa yang menyebabkan masalah ini tetapi saya akan meninggalkan catatan ini di sini kalau-kalau ada yang mengalami masalah yang sama saya lakukan.

Sly_cardinal
sumber
Terima kasih! Ini adalah masalah yang cukup konyol dan sulit untuk mencari tahu apa masalahnya. Mungkin Scriptays harus mempertimbangkan cara yang lebih baik untuk menangani enum.
ChickenFeet
25

Saya membuatnya bekerja menggunakan kode berikut.

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

Jika Anda memberikan nilai string ke enum Anda, gips lurus berfungsi dengan baik.

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

const color = "Green";
const colorEnum = color as Color;
alexania
sumber
1
Sangat sederhana. Bagus!
Bernoulli,
1
Ini bisa menyesatkan karena tidak melindungi dari warna yang tidak valid. const colorEnum = "Blue" as Colortidak akan salah, dan Anda akan berpikir bahwa colorEnumitu OK. Tetapi jika Anda ke console.logsana, Anda akan melihat "Biru". Jawaban Artru bagus, karena colorEnumakan undefined- dan Anda kemudian dapat memeriksanya secara khusus.
M Falanga
20

Mengingat Anda menggunakan naskah: Banyak solusi di atas mungkin tidak berfungsi atau terlalu rumit.

Situasi : String tidak sama dengan nilai enum (casing berbeda)

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

Cukup gunakan:

const color = "green" as Color
Nick N.
sumber
15

Saya juga mengalami kesalahan kompiler yang sama. Hanya sedikit variasi dari pendekatan Sly_cardinal.

var color: Color = Color[<string>colorId];
Chris
sumber
Sebagai tambahan: Jika Anda memiliki skrip enum yang diisi oleh lapisan javascript yang membuat serial enum sebagai string (katakan misalnya Asp Web API via AngularJS), Anda dapat melakukan myProp.color = Color[<string><any>myProp.color] Cheers
Victor
1
Ini harus menjadi jawaban yang dikenal.
Miroslav Popov
9

Jika kompiler TypeScript tahu bahwa tipe variabel adalah string maka ini berfungsi:

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

Kalau tidak, Anda harus secara eksplisit mengonversinya menjadi string (untuk menghindari peringatan kompiler):

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

Saat runtime kedua solusi akan bekerja.

Luka
sumber
3
mengapa tidak menggunakan typecast <string>colorNamesaja "" + colorName?
SergeyT
7

Ada banyak informasi campuran dalam pertanyaan ini, jadi mari kita bahas seluruh implementasi untuk TypeScript 2.x + di Panduan Nick untuk Menggunakan Enums dalam Model dengan TypeScript .

Panduan ini untuk: orang yang membuat kode sisi-klien yang menelan serangkaian string yang diketahui dari server yang akan dimodelkan dengan mudah sebagai Enum di sisi klien.

Tentukan enum

Mari kita mulai dengan enum. Seharusnya terlihat seperti ini:

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

Dua hal yang perlu diperhatikan di sini:

  1. Kami secara eksplisit mendeklarasikan ini sebagai kasus enum yang didukung string yang memungkinkan kami untuk membuat instance dengan string, bukan beberapa angka yang tidak terkait lainnya.

  2. Kami telah menambahkan sebuah opsi yang mungkin atau mungkin tidak ada pada model server kami: UNKNOWN. Ini dapat ditangani seolah- undefinedolah Anda lebih suka, tetapi saya ingin menghindari | undefinedjenis bila memungkinkan untuk menyederhanakan penanganan.

Hal terbaik dari memiliki UNKNOWNkasing adalah Anda dapat benar-benar jelas tentangnya dalam kode dan membuat gaya untuk kasing enum yang tidak diketahui berwarna merah terang dan berkilau sehingga Anda tahu Anda tidak menangani sesuatu dengan benar.

Pecahkan enum

Anda mungkin menggunakan enum ini tertanam dalam model lain, atau sendirian, tetapi Anda harus mengurai enum yang diketik string-y dari JSON atau XML (ha) ke rekan Anda yang sangat diketik. Ketika disematkan dalam model lain, parser ini hidup di konstruktor kelas.

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

  return type;
}

Jika enum diurai dengan benar, itu akan berakhir sebagai jenis yang tepat. Jika tidak, itu akan terjadi undefineddan Anda dapat mencegatnya dan mengembalikan UNKNOWNkasing Anda . Jika Anda lebih suka menggunakan undefinedsebagai kasing yang tidak dikenal, Anda bisa mengembalikan hasil apa pun dari percobaan enum parsing.

Dari sana, itu hanya masalah menggunakan fungsi parse dan menggunakan variabel yang baru Anda ketikkan.

const strongIssueType: IssueType = parseIssueType('ACTION');
// IssueType.ACTION
const wrongIssueType: IssueType = parseIssueType('UNEXPECTED');
// IssueType.UNKNOWN
Nick
sumber
6
Sayangnya, ini tampaknya tidak benar atau, setidaknya, tidak dapat digeneralisasikan. Ini berfungsi karena kunci Anda sama dengan string yang ditugaskan. Namun, jika mereka, seperti dalam kasus saya, berbeda, ini tidak berfungsi. Dalam kata-kata dokumentasi : "Perlu diingat bahwa anggota string enum tidak mendapatkan pemetaan terbalik yang dihasilkan sama sekali." Kode Anda akan dikompilasi ke sesuatu seperti IssueType["REPS"]="REPS". Jika Anda mendefinisikan enum Anda sedikit berbeda, katakanlah, REPS="reps"ini akan menghasilkan IssueType["REPS"]="reps"yang ...
altocumulus
... selalu kembali IssueType.UNKNOWNkarena tidak ada kunci repsdi enum Anda. Sayang sekali, saya masih tidak menemukan solusi untuk ini karena string saya mengandung tanda hubung yang membuat mereka tidak dapat digunakan sebagai kunci.
altocumulus
Akhirnya, saya menemukan solusi dalam jawaban ini dengan meyakinkan kompiler bahwa ini bukan string enum. Mungkin perlu mengedit info ini menjadi jawaban Anda sendiri.
altocumulus
7

Saya mencari jawaban yang bisa mendapatkan enumdari string, tetapi dalam kasus saya, nilai enum memiliki pasangan nilai string yang berbeda. OP memiliki enum sederhana untuk Color, tetapi saya memiliki sesuatu yang berbeda:

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

Ketika Anda mencoba menyelesaikan Gender.CantTelldengan "Can't tell"string, itu kembali undefineddengan jawaban yang asli.

Jawaban lain

Pada dasarnya, saya datang dengan jawaban lain, sangat terinspirasi oleh jawaban ini :

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

Catatan

  • Kami mengambil hasil pertama dari filter, dengan asumsi klien melewati string yang valid dari enum. Jika tidak demikian, undefinedakan dikembalikan.
  • Kami cor enumObjuntuk any, karena dengan naskah 3.0+ (saat ini menggunakan naskah 3,5), yang enumObjdiselesaikan sebagai unknown.

Contoh Penggunaan

const cantTellStr = "Can't tell";

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

Catatan: Dan, seperti yang ditunjukkan seseorang dalam komentar, saya juga ingin menggunakan noImplicitAny.

Versi terbaru

Tidak ada cor ke anydan pengetikan yang tepat.

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

Juga, versi yang diperbarui memiliki cara yang lebih mudah untuk menyebutnya dan lebih mudah dibaca:

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

Saya perlu tahu cara mengulang nilai enum (sedang menguji banyak permutasi dari beberapa enum) dan saya menemukan ini berfungsi dengan baik:

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
}

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

mikeb
sumber
Jawaban ini genious! Suka. Terutama cara Anda membuat enum dari tali. Ini bisa menghemat banyak pengetikan saat menguji enum atau case lainnya.
Florian Leitgeb
Ya, saya menggunakan ini dengan Jest eachuntuk menguji setiap kasus enum dengan hanya satu metode
mikeb
3

Enum

enum MyEnum {
    First,
    Second,
    Three
}

Penggunaan sampel

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

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

Abaikan parse peka huruf besar-kecil

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;
    }
}
Очир Дармаев
sumber
Siapa pun yang memiliki enum seperti saya harus memasukkan return enumType[property];kasus ketika item enum Anda terlihat sepertiSkills = "anyvalue"
neustart47
@ neustart47 dapatkah Anda mengajukan pertanyaan?
Очир Дармаев
itu bukan pertanyaan. Saya baru saja menyebutkan beberapa perubahan untuk siapa saja yang mencari kasus yang sama seperti saya. Jawaban Anda benar.
neustart47
2

Enum yang dibuat dengan cara Anda dikompilasi menjadi objek yang menyimpan pemetaan maju (name -> value)dan mundur (value -> name). Seperti yang bisa kita amati dari tangkapan layar chrome devtools ini:

masukkan deskripsi gambar di sini

Berikut adalah contoh cara kerja pemetaan ganda dan cara saling melemparkan:

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
sumber
1

Coba ini

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

Itu berfungsi dengan baik untuk versi 3.5.3

Oyeme
sumber
0

Jika Anda menggunakan ruang nama untuk memperluas fungsionalitas enum Anda, maka Anda juga dapat melakukan sesuatu seperti

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

dan gunakan seperti ini

  Color.getInstance('Red');
andrei.b
sumber
0

variasi lain bisa

const green= "Green";

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