Як перетворити рядок в перерахунок у TypeScript?


311

Я визначив наступний перелік у TypeScript:

enum Color{
    Red, Green
}

Тепер у своїй функції я отримую колір у вигляді рядка. Я спробував наступний код:

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

Як я можу перетворити це значення на перерахунок?

Відповіді:


430

Перерахунки в 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)
Войт

3
@Vojta сказав правильно. Її не працює у VS 2012. Цей працював, але var color: Color = (<any> Color) [зелений];
Faisal Mq

3
Тут не працює і офіційна документація, схоже, підтверджує, що: typecriptlang.org/docs/handbook/release-notes/…
Pieter De Bie

26
Обов’язково використовуйте це, якщо --noImplicitAny var color : Color = Color[green as keyof typeof Color];
Йонас

2
@ Naxos84 Дивіться мій answear stackoverflow.com/a/56076148/294242
Jonas

122

Станом на Typescript 2.1 рядкові клавіші в перерахунках сильно набираються. 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'
Домінік

2
Відповідь в одному рядку:const color: Color = Color[colorString as keyof typeof Color];
cscan

38

Якщо ви впевнені, що вхідний рядок відповідає збігу з кольором 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 Enum працює з числовими індексами, тобто let c = Color[0], але не зі строковими індексами типу let c = Color["string"]. Це відоме обмеження командою Microsoft для більш загальних індексних рядкових рядкових індексів .


Ви також можете передати на <keyof typeof Color>. Також "0" також неправильний ввід, але він не повернеться невизначеним, тому перевірте typeof mayBeColor === 'число'
Квентін 2,

@ Quentin2, що з числовим рядком? тобто typeof '0'має бутиstring
Патрік

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

Цей приклад працює з --noImplicitAnyTypeScript

Джерела:
https://github.com/Microsoft/TypeScript/isissue/13775#issuecomment-276381229 https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types


Я не знаю чому, але це рішення не працює над const enum (використовуючи Typescript 3.8.3)
Робін-Худі

30

Це зауваження стосується basarat в відповідь , а не оригінальні питання.

У мене був незвичайний випуск у власному проекті, коли компілятор давав помилку, приблизно еквівалентну "не вдається перетворити рядок у колір", використовуючи еквівалент цього коду:

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

Я виявив, що зараження типу компілятора заплутується, і думав, що colorIdце значення перерахунку, а не ідентифікатор. Щоб вирішити проблему, мені довелося передати ідентифікатор як рядок:

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

Я не впевнений, що викликало проблему, але я залишу цю записку тут, якщо хтось зіткнеться з тією ж проблемою, що і я.


Дякую! Це досить дурне питання, і важко зрозуміти, у чому проблема. Можливо, Typescript повинен розглянути можливість створення кращого способу поводження з перевагами.
ChickenFeet

25

У мене це працює за допомогою наступного коду.

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

23

Якщо ви надаєте рядкові значення для перерахунку, прямий ролик працює просто чудово.

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

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

1
Дуже просто. Приємно!
Бернуллі ІТ

1
Це може ввести в оману, оскільки воно не захищає від недійсних кольорів. const colorEnum = "Blue" as Colorне помилиться, і вам залишиться думати, що colorEnumце нормально. Але якби ви були до console.logнього, ви побачили б "Блакитного". Відповідь Артру приємна, бо colorEnumбуде undefined- і ви зможете конкретно перевірити це.
М Фаланга

20

З огляду на те, що ви використовуєте машинопис: Багато з вищевказаних рішень можуть не працювати або надмірно складні.

Ситуація : рядки не збігаються із значеннями enum (кожух відрізняється)

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

Просто використовуйте:

const color = "green" as Color

15

Я також зіткнувся з тією ж помилкою компілятора. Лише трохи коротша варіація підходу Sly_cardinal.

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

Як додаток: Якщо у вас є encript шрифту, заповнений шаром javascript, який серіалізував 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?
СергійТ

7

У цьому питанні є багато змішаної інформації, тому давайте висвітлимо всю реалізацію для TypeScript 2.x + у Посібнику Ніка щодо використання Enums у моделях з TypeScript .

Цей посібник призначений для: людей, які створюють код на стороні клієнта, який поглинає набір відомих рядків із сервера, який було б зручно моделювати як Enum на стороні клієнта.

Визначте перерахунок

Почнемо з перерахунку. Це має виглядати приблизно так:

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

Тут слід зазначити дві речі:

  1. Ми явно декларуємо це як обкладені рядками випадки перерахунків, що дозволяє нам інстанціювати їх рядками, а не деякими іншими незв'язаними числами.

  2. Ми додали опцію , яка може або не може існувати на нашій моделі сервера: UNKNOWN. Це можна обробляти так, як undefinedніби ви хочете, але я люблю уникати | undefinedтипів, коли це можливо, щоб спростити обробку.

Чудова річ у UNKNOWNсправі - це те, що ти можеш бути дійсно очевидним щодо цього у коді та створювати стилі для невідомих випадків перерахунку яскраво-червоними та миготливими, щоб ти знав, що ти щось не правильно поводиш.

Розбираємо перерахунок

Можливо, ви використовуєте цей перерахунок, вбудований в іншу модель, або зовсім поодинці, але вам доведеться проаналізувати переписаний рядком y від JSON або XML (га) у ваш сильно набраний колега. Вбудований в іншу модель, цей аналізатор живе в конструкторі класу.

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

  return type;
}

Якщо перерахунок буде правильно проаналізований, він закінчиться належним типом. В іншому випадку це буде, undefinedі ви можете перехопити його і повернути свою UNKNOWNсправу. Якщо ви вважаєте за краще використовувати undefinedяк невідомий випадок, ви можете просто повернути будь-який результат від спроби синтаксичного аналізу.

Звідси лише питання використання функції розбору та використання вашої щойно сильної введеної змінної.

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

6
На жаль, це здається невірним або, принаймні, не узагальненим. Це працює, тому що ваші клавіші рівні рядкам, яким вони були призначені. Якщо вони, як і в моєму випадку, відрізняються, однак, це не працює. За словами документації : "Майте на увазі, що члени перерахунків рядків взагалі не отримують зворотне відображення". Ваш код складеться до чогось подібного IssueType["REPS"]="REPS". Якби ви визначили свій перелік трохи іншим, скажімо, REPS="reps"це дасть результат, IssueType["REPS"]="reps"який би ...
altocumulus

... завжди повертайтеся, IssueType.UNKNOWNтому що repsв вашому перерахунку немає ключа . Шкода, що я все ще не знайшов робочого рішення для цього, оскільки мої рядки містять дефіси, що робить їх непридатними як клавіші.
altocumulus

Нарешті, я знайшов рішення у цій відповіді , переконавши укладача, що це не рядковий перелік. Можливо, варто відредагувати цю інформацію у власній відповіді.
altocumulus

6

Мені потрібно було знати, як перебирати значення перерахунків (було тестування безлічі перестановок декількох перерахунків), і я виявив, що це добре працює:

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/


Ця відповідь геніальна! Любіть це. Особливо те, як ви робите перерахунок із струни. Це дозволяє заощадити набравши під час тестування переліків чи інших справ.
Флоріан

Так, я використовую це з Jest's, eachщоб перевірити кожен випадок перерахунку лише одним методом
mikeb

6

Я шукав відповідь, яку можна отримати enumз string, але в моєму випадку значення перерахунків мали різні аналоги рядкових значень. В ОП був простий перелік 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, якщо припустити , що клієнт проходить дійсну рядок з перерахування. Якщо це не так, undefinedбуде повернуто.
  • Ми звертаємося enumObjдо цього any, оскільки з TypeScript 3.0+ (зараз використовується TypeScript 3.5), enumObjце вирішено як unknown.

Приклад використання

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");

3

Енум

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

Кожен, хто має перерахунок, як я, повинен поставити return enumType[property];справу на вигляд вашого елемента перерахункуSkills = "anyvalue"
neustart47

@ neustart47 Ви можете, будь ласка, задати питання?
Очир Дармаєв

це не питання. Я лише згадав деякі зміни для тих, хто шукає той самий випадок, що і я. Ваша відповідь правильна.
neustart47

2

Енуми, створені так, як ви робили, компілюються в об'єкт, який зберігає як прямі, так (name -> value)і зворотні (value -> name)відображення. Як ми можемо спостерігати з цього хромового скріншота Devtools:

введіть тут опис зображення

Ось приклад того, як працює подвійне картографування та як передавати з одного на інший:

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

Спробуйте це

var color: Color = (Колір як будь-який) ["Зелений];

Це чудово працює для версії 3.5.3


0

Якщо ви використовуєте простори імен для розширення функціональності вашого переліку, ви також можете зробити щось подібне

    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');

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.