ฉันจะแปลงสตริงเป็น enum ใน TypeScript ได้อย่างไร


312

ฉันได้กำหนด enum ต่อไปนี้ใน TypeScript:

enum Color{
    Red, Green
}

ตอนนี้ในฟังก์ชั่นของฉันฉันได้รับสีเป็นสตริง ฉันลองใช้รหัสต่อไปนี้:

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

ฉันจะแปลงค่านั้นเป็น enum ได้อย่างไร

คำตอบ:


431

Enums ใน TypeScript 0.9 เป็นสตริง + หมายเลข คุณไม่ควรต้องมีการยืนยันประเภทสำหรับการแปลงอย่างง่าย:

enum Color{
    Red, Green
}

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

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

ลองออนไลน์

ฉันมีเอกสารเกี่ยวกับสิ่งนี้และรูปแบบ Enum อื่น ๆ ในหนังสือ OSS ของฉัน: https://basarat.gitbook.io/typescript/type-system/enums


111
สิ่งนี้ใช้ไม่ได้กับ--noImplicitAny(ใน VS ไม่ได้เลือก "อนุญาตประเภท" ใด ๆ "โดยนัย) มันผลิตerror TS7017: Index signature of object type implicitly has an 'any' type.สำหรับฉันนี้ทำงาน: var color: Color = (<any>Color)[green];(ทดสอบกับรุ่น 1.4)
Vojta

3
@Vojta พูดถูก มันไม่ทำงานใน VS 2012 อันนี้ใช้ได้ แต่สี var: Color = (<any> สี) [สีเขียว]
Faisal Mq

3
มันไม่ทำงานที่นี่ทั้งเอกสารอย่างเป็นทางการดูเหมือนจะยืนยันว่า: typescriptlang.org/docs/handbook/release-notes/ …
Pieter De Bie

26
ตรวจสอบให้แน่ใจว่าได้ใช้สิ่งนี้หาก --noImplicitAny var color : Color = Color[green as keyof typeof Color];
Jonas


123

ในฐานะที่เป็นของสตริงอักขระ typescript 2.1 ใน enums จะพิมพ์อย่างยิ่ง keyof typeofใช้เพื่อรับข้อมูลเกี่ยวกับคีย์สตริงที่มีอยู่ ( 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
ดังนั้นเราสามารถใช้ typecast:let s = "Green"; let typedColor = <keyof typeof Color> s;
SergeyT

ใช่และแทนที่letด้วยconstจะทำงานโดยไม่ต้องหล่อ อัปเดตตัวอย่างเพื่อชี้แจงสิ่งนี้ ขอบคุณ @SergeyT
Victor

1
typedColorString = Color["Black"];ตอนนี้จะกลับมาerror TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'
Dominik

2
คำตอบหนึ่งบรรทัด:const color: Color = Color[colorString as keyof typeof Color];
cscan

38

หากคุณแน่ใจว่าสตริงอินพุตมีการจับคู่แบบตรงกับ Color enum ให้ใช้:

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

ในกรณีที่สตริงอินพุตอาจไม่ตรงกับ Enum ใช้:

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

สนามเด็กเล่น


ถ้าเราไม่ได้โยนenumไป<any>พิมพ์แล้ว typescript จะแสดงข้อผิดพลาด:

องค์ประกอบโดยนัยมีชนิด 'ใด ๆ ' เนื่องจากนิพจน์ดัชนีไม่ใช่ชนิด 'หมายเลข'

มันหมายความว่าโดยปกติชนิด typescript Enum ทำงานร่วมกับดัชนีจำนวนเช่น แต่ไม่ได้มีการจัดทำดัชนีสายเช่นlet c = Color[0] let c = Color["string"]นี่คือข้อ จำกัด ที่รู้จักกันโดยทีมงานไมโครซอฟท์สำหรับปัญหาทั่วไปมากขึ้นดัชนีสตริงวัตถุ


คุณสามารถส่งไปยัง <keyof typeof Color> นอกจากนี้ "0" ก็มีการป้อนที่ไม่ถูกต้องเช่นกัน แต่จะไม่ส่งคืนไม่ได้กำหนดดังนั้นให้ตรวจสอบประเภทของ mayBeColor === 'number'
Quentin 2

@ Quentin2 แล้วสตริงตัวเลขล่ะ? เช่นtypeof '0'ควรเป็นstring
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

ตัวอย่างนี้ใช้ได้กับ--noImplicitAnyใน TypeScript

แหล่งที่มา:
https://github.com/Microsoft/TypeScript/issues/13775#issuecomment-276381229 https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types


ผมไม่ทราบว่าทำไม แต่การแก้ปัญหานี้ไม่ได้ทำงานบนบนconst enum (ใช้ typescript 3.8.3)
โรบิน-Hoodie

30

บันทึกนี้เกี่ยวข้องกับคำตอบของบารารัตน์ไม่ใช่คำถามดั้งเดิม

ฉันมีปัญหาแปลก ๆ ในโครงการของฉันซึ่งคอมไพเลอร์ให้ข้อผิดพลาดโดยประมาณเทียบเท่ากับ "ไม่สามารถแปลงสตริงเป็นสี" ได้โดยใช้โค้ดนี้:

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

ฉันพบว่าการอนุมานประเภทคอมไพเลอร์เริ่มสับสนและคิดว่าcolorIdเป็นค่า enum ไม่ใช่ ID เพื่อแก้ไขปัญหาฉันต้องแปลง ID เป็นสตริง:

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

ฉันไม่แน่ใจว่าสิ่งที่ทำให้เกิดปัญหา แต่ฉันจะออกจากบันทึกนี้ที่นี่ในกรณีที่ทุกคนพบปัญหาเดียวกันกับที่ฉันทำ


ขอบคุณ! นี่เป็นปัญหาที่ค่อนข้างงี่เง่าและยากที่จะเข้าใจว่าปัญหาคืออะไร Maybe Typescript ควรพิจารณาวิธีการจัดการ enums ที่ดีกว่า
ChickenFeet

25

ฉันได้มันทำงานโดยใช้รหัสต่อไปนี้

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

23

หากคุณกำหนดค่าสตริงให้กับ enum ของคุณการส่งแบบตรงจะใช้งานได้ดี

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

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

1
ง่ายมาก. ดี!
Bernoulli ไอที

1
สิ่งนี้อาจทำให้เข้าใจผิดเนื่องจากไม่ได้ป้องกันสีที่ไม่ถูกต้อง const colorEnum = "Blue" as Colorจะไม่เกิดข้อผิดพลาดและคุณคงคิดว่าcolorEnumไม่เป็นไร แต่ถ้าคุณไปconsole.logคุณจะเห็น "สีฟ้า" คำตอบของ Artruนั้นดีเพราะcolorEnumจะเป็นundefined- แล้วคุณสามารถตรวจสอบได้โดยเฉพาะ
M Falanga

20

ให้คุณใช้ typescript: การแก้ปัญหาข้างต้นอาจไม่ทำงานหรือซับซ้อนเกินไป

สถานการณ์ : สตริงไม่เหมือนกับค่า enum (ปลอกแตกต่างกัน)

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

เพียงใช้:

const color = "green" as Color

15

ฉันยังพบข้อผิดพลาดของคอมไพเลอร์เดียวกัน รูปแบบที่เปลี่ยนแปลงเล็กน้อยของแนวทางของ Sly_cardinal

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

นอกจากนี้: ในกรณีที่คุณมี typescript enum ที่เต็มไปด้วยเลเยอร์ javascript ซึ่งทำให้ serial enum เป็นสตริง (เช่น Asp Web API ผ่าน AngularJS) คุณสามารถทำ myProp.color = Color[<string><any>myProp.color] เสียงไชโย
Victor

1
นี่จะต้องเป็นคำตอบที่ได้รับการยอมรับ
มิโรสลาฟโปปอฟ

9

หากคอมไพเลอร์ TypeScript รู้ว่าประเภทของตัวแปรเป็นสตริงการทำงานก็จะเป็นเช่นนี้:

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

มิฉะนั้นคุณควรแปลงให้เป็นสตริง (เพื่อหลีกเลี่ยงคำเตือนของคอมไพเลอร์):

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

ที่รันไทม์โซลูชั่นทั้งสองจะทำงาน


3
ทำไมไม่ใช้ typecast <string>colorNameแทน"" + colorNameล่ะ
SergeyT

7

มีข้อมูลจำนวนมากที่ผสมในคำถามนี้คือเพื่อให้ครอบคลุมการใช้งานทั้ง typescript 2.x + ในคู่มือของนิคเพื่อการใช้ Enums ในรุ่นที่มี typescript

คู่มือนี้มีไว้สำหรับ: ผู้ที่สร้างรหัสฝั่งไคลเอ็นต์ที่รับชุดสตริงที่รู้จักจากเซิร์ฟเวอร์ซึ่งจะสร้างแบบจำลองเป็น Enum ในฝั่งไคลเอ็นต์ได้อย่างสะดวก

กำหนด enum

มาเริ่มกันที่ Enum กันดีกว่า ควรมีลักษณะดังนี้:

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

สิ่งที่ควรทราบสองประการ:

  1. เรากำลังประกาศสิ่งเหล่านี้อย่างชัดเจนว่าเป็นกรณี enum ที่สนับสนุนสตริงซึ่งทำให้เราสามารถยกตัวอย่างพวกเขาด้วยสตริงไม่ใช่ตัวเลขที่ไม่เกี่ยวข้องอื่น ๆ

  2. UNKNOWNเราได้เพิ่มตัวเลือกที่อาจจะหรืออาจจะไม่ได้อยู่ในรูปแบบเซิร์ฟเวอร์ของเรา: สิ่งนี้สามารถจัดการได้ราวกับundefinedว่าคุณต้องการ แต่ฉันชอบที่จะหลีกเลี่ยง| undefinedกับประเภทเมื่อใดก็ตามที่เป็นไปได้เพื่อให้การจัดการง่ายขึ้น

สิ่งที่ยอดเยี่ยมเกี่ยวกับการมีUNKNOWNเคสคือคุณสามารถเห็นได้ชัดในโค้ดและสร้างสไตล์สำหรับเคสที่ไม่รู้จัก enum สีแดงสดและบล็อตเพื่อให้คุณรู้ว่าคุณไม่ได้จัดการอะไรอย่างถูกต้อง

แยกวิเคราะห์ enum

คุณอาจใช้ enum นี้ฝังอยู่ในรุ่นอื่นหรืออยู่คนเดียว แต่คุณจะต้องแยกวิเคราะห์ enum แบบสตริง - y จาก JSON หรือ XML (ฮา) ลงในคู่ที่พิมพ์ของคุณอย่างยิ่ง เมื่อฝังในโมเดลอื่นตัวแยกวิเคราะห์นี้จะใช้ชีวิตในตัวสร้างคลาส

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

  return type;
}

หากแยกวิเคราะห์ enum ได้อย่างถูกต้องจะสิ้นสุดลงตามประเภทที่เหมาะสม มิฉะนั้นจะเกิดขึ้นundefinedและคุณสามารถดักจับและคืนUNKNOWNเคสของคุณได้ หากคุณต้องการใช้undefinedเป็นกรณีที่คุณไม่รู้จักคุณสามารถส่งคืนผลลัพธ์ใด ๆ จากการวิเคราะห์คำที่พยายามแยกวิเคราะห์

จากตรงนั้นเป็นเพียงเรื่องของการใช้ฟังก์ชั่นการแยกวิเคราะห์และการใช้ตัวแปรที่พิมพ์ใหม่ที่แข็งแกร่งของคุณ

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

6
น่าเสียดายที่สิ่งนี้ดูเหมือนจะไม่ถูกต้องหรืออย่างน้อยก็ไม่สามารถมองเห็นได้ มันทำงานได้เพราะกุญแจของคุณเท่ากับสายที่พวกเขาได้รับมอบหมาย อย่างไรก็ตามหากพวกเขาแตกต่างกันเช่นนี้ในกรณีของฉันมันไม่ทำงาน ในคำของเอกสาร : "โปรดจำไว้ว่าสมาชิกสตริง enum ไม่ได้รับการแม็พย้อนกลับที่สร้างขึ้นเลย" IssueType["REPS"]="REPS"รหัสของคุณจะรวบรวมเพื่อสิ่งที่ต้องการ ถ้าคุณนิยามค่า enum ของคุณแตกต่างกันเล็กน้อยพูดREPS="reps"นี่จะให้ผลIssueType["REPS"]="reps"ซึ่งจะ ...
altocumulus

... กลับมาเสมอIssueType.UNKNOWNเพราะไม่มีคีย์repsใน enum ของคุณ น่าเสียดายที่ฉันยังไม่พบวิธีแก้ปัญหาที่ใช้งานได้เนื่องจากสตริงของฉันมีเครื่องหมายขีดกลางซึ่งทำให้พวกเขาใช้เป็นคีย์ไม่ได้
altocumulus

ในที่สุดฉันพบวิธีแก้ปัญหาในคำตอบนี้โดยการโน้มน้าวใจคอมไพเลอร์ว่านี่ไม่ใช่ string enum มันอาจคุ้มค่าที่จะแก้ไขข้อมูลนี้ในคำตอบของคุณ
altocumulus

7

ฉันกำลังมองหาคำตอบที่สามารถหาได้enumจาก a stringแต่ในกรณีของฉันค่า enums มีค่าสตริงที่ต่างกัน OP มี enum ง่ายสำหรับColorแต่ฉันมีสิ่งที่แตกต่าง:

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

เมื่อคุณพยายามที่จะแก้ไขGender.CantTellด้วย"Can't tell"สตริงมันจะกลับมาundefinedพร้อมกับคำตอบเดิม

คำตอบอื่น

โดยพื้นฐานแล้วฉันมากับคำตอบอื่นที่ได้แรงบันดาลใจจากคำตอบนี้ :

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

หมายเหตุ

  • เรารับผลลัพธ์แรกของfilterสมมติว่าไคลเอนต์กำลังส่งสตริงที่ถูกต้องจาก enum หากไม่ใช่กรณีดังกล่าวundefinedจะถูกส่งคืน
  • เราได้เหวี่ยงenumObjไปanyเพราะมี typescript 3.0 ขึ้นไป (ปัจจุบันใช้ typescript 3.5) ที่ได้รับการแก้ไขenumObjunknown

ตัวอย่างการใช้งาน

const cantTellStr = "Can't tell";

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

หมายเหตุ: noImplicitAnyและเป็นคนชี้ให้เห็นในความคิดเห็นผมยังอยากจะใช้

อัปเดตเวอร์ชันแล้ว

ไม่มีanyการคัดลอกและพิมพ์ที่เหมาะสม

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

นอกจากนี้เวอร์ชันที่อัปเดตมีวิธีที่ง่ายกว่าในการโทรหาและอ่านได้ง่ายขึ้น:

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

6

ฉันจำเป็นต้องรู้วิธีวนลูปมากกว่าค่า enum (คือการทดสอบพีชคณิตหลาย enums) และฉันพบว่ามันทำงานได้ดี:

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
}

ที่มา: https://blog.mikeski.net/development/javascript/typescript-enums-to-from-string/


คำตอบนี้มีความสุขุม! รักมัน โดยเฉพาะอย่างยิ่งวิธีที่คุณทำ Enum จากสตริง วิธีนี้จะช่วยให้คุณประหยัดการพิมพ์ได้มากเมื่อทำการทดสอบ enums หรือกรณีอื่น ๆ
Florian

ใช่ฉันใช้นี้กับเจ็ทของeachเพื่อทดสอบทุกกรณี enum เดียวมีเพียงวิธีการหนึ่ง
mikeb

3

enum

enum MyEnum {
    First,
    Second,
    Three
}

ตัวอย่างการใช้งาน

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

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

ไม่สนใจการแยกวิเคราะห์ตัวพิมพ์เล็กและตัวพิมพ์ใหญ่

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

ทุกคนที่มี enum อย่างฉันควรใส่ไว้return enumType[property];ในกรณีที่รายการ enum ของคุณดูเหมือนSkills = "anyvalue"
neustart47 47

@ neustart47 คุณช่วยถามคำถามได้ไหม?
ОчирДармаев

มันไม่ใช่คำถาม ฉันเพิ่งพูดถึงการเปลี่ยนแปลงบางอย่างสำหรับใครก็ตามที่กำลังค้นหากรณีเดียวกันกับที่ฉันมี คำตอบของคุณถูกต้อง
neustart47

2

Enums ที่สร้างขึ้นในแบบที่คุณทำจะถูกรวบรวมเป็นวัตถุที่เก็บทั้งการแมปไปข้างหน้า(name -> value)และย้อนกลับ (value -> name)ตามที่เราสามารถสังเกตได้จากภาพหน้าจอของ devtools chrome นี้:

ป้อนคำอธิบายรูปภาพที่นี่

นี่คือตัวอย่างการทำงานของการจับคู่คู่และวิธีส่งจากที่หนึ่งไปยังอีกที่หนึ่ง:

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


0

หากคุณกำลังใช้เนมสเปซเพื่อขยายการทำงานของ enum ของคุณคุณสามารถทำสิ่งต่างๆเช่น

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

และใช้มันเช่นนี้

  Color.getInstance('Red');

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.