Bir dizeyi TypeScript'te numaralandırmaya nasıl dönüştürebilirim?


312

TypeScript içinde aşağıdaki numaralandırma tanımladım:

enum Color{
    Red, Green
}

Şimdi benim fonksiyonumda bir dize olarak renk alıyorum. Aşağıdaki kodu denedim:

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

Bu değeri nasıl bir numaraya dönüştürebilirim?

Yanıtlar:


431

TypeScript 0.9'daki numaralandırmalar dize + sayı tabanlıdır. Basit dönüşümler için tür onayına gerek yoktur:

enum Color{
    Red, Green
}

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

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

Çevrimiçi deneyin

OSS kitabımda bu ve diğer Enum kalıpları hakkında belgesel var: https://basarat.gitbook.io/typescript/type-system/enums


112
Bu ile çalışmaz --noImplicitAny(VS işaretlenmemiş "Örtük 'herhangi bir' tür" izin ver). Benim için bunu üretti error TS7017: Index signature of object type implicitly has an 'any' type.: var color: Color = (<any>Color)[green];(1.4 sürümü ile test edildi)
Vojta

3
@Vojta doğru dedi. VS 2012'de çalışmıyor. Bu çalıştı ama var renk: Renk = (<any> Renk) [yeşil];
Faisal Mq

3
Burada da çalışmıyor, resmi belgelerin aşağıdakileri doğruladığı görülüyor: typescriptlang.org/docs/handbook/release-notes/…
Pieter De Bie

26
--NoImplicitAnyvar color : Color = Color[green as keyof typeof Color];
Jonas

2
@ Naxos84 Cevabımı görüntüle stackoverflow.com/a/56076148/294242
Jonas

123

Typcript 2.1'den itibaren numaralandırmalarda dize anahtarları kuvvetle yazılır. keyof typeofkullanılabilir dize tuşları ( 1 ) hakkında bilgi almak için kullanılır :

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
Bu yüzden typecast'i kullanabiliriz:let s = "Green"; let typedColor = <keyof typeof Color> s;
SergeyT

Evet, ve değiştirilmesi letile constçevrim yapmadan irade çalışması. Bunu açıklığa kavuşturmak için örnek güncellendi. @SergeyT
Victor

1
typedColorString = Color["Black"];şimdi geri dönüyorerror TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'
Dominik

2
Bir satır cevap:const color: Color = Color[colorString as keyof typeof Color];
cscan

38

Bir giriş dizesinin Color enum ile tam olarak eşleştiğinden eminseniz şunu kullanın:

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

Bir giriş dizesinin Enum ile eşleşmemesi durumunda şunu kullanın:

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

Oyun alanı


Kadroya yoksa enumüzere <any>sonra yazın typescript hatası gösterecektir:

Dizin ifadesi 'sayı' türünde olmadığından, öğe dolaylı olarak 'herhangi' türüne sahip.

Bu, TypeScript Enum türünün varsayılan olarak sayı dizinleri ile çalıştığı let c = Color[0], ancak gibi dize dizinleriyle çalışmadığı anlamına gelir let c = Color["string"]. Bu, Microsoft ekibi tarafından daha genel sorun Nesne dizesi dizinleri için bilinen bir kısıtlamadır .


Ayrıca <Color of type of key> öğesine de yayınlayabilirsiniz. Ayrıca "0" girişi de yanlış ancak tanımsız olarak döndürülmeyecek, bu nedenle mayeofElor === 'number' türünü kontrol edin
Quentin 2

@ Quentin2 sayısal bir dize ne olacak? yani typeof '0'olmalı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

Bu örnek --noImplicitAny, TypeScript'te çalışır

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


Neden bilmiyorum, ama bu çözüm bir const enum üzerinde çalışmıyor (Typcript 3.8.3 kullanarak)
Robin-Hoodie

30

Bu not , orijinal sorunun değil, basarat'ın cevabı ile ilgilidir .

Derleyici kabaca bu kod eşdeğerini kullanarak kabaca "dize renk dönüştüremiyorum" eşdeğer bir hata veriyordu kendi projesinde garip bir sorun vardı:

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

Derleyici türü çıkarımın karıştığını ve bunun colorIdbir kimlik değil, bir numara değeri olduğunu düşündüm . Sorunu çözmek için kimliği bir dize olarak yayınlamak zorunda kaldım:

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

Soruna neyin sebep olduğundan emin değilim, ancak herkesin yaptığım aynı sorunla karşılaşması durumunda bu notu burada bırakacağım.


Teşekkür ederim! Bu oldukça saçma bir konudur ve sorunun ne olduğunu anlamak zordur.
ChickenFeet

25

Aşağıdaki kodu kullanarak çalıştım.

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

23

Numaralandırmanıza dize değerleri sağlarsanız, düz bir yayın düzgün çalışır.

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

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

1
Çok basit. Güzel!
Bernoulli IT

1
Geçersiz renklere karşı koruma sağlamadığı için bu yanıltıcı olabilir. const colorEnum = "Blue" as Colorhata yapmazsınız ve sorun olmadığını colorEnumdüşünürsünüz. Ama eğer console.logona olsaydın, "Mavi" görürdün. Artru'nun cevabı güzel, çünkü colorEnumolacak undefined- ve sonra bunu özellikle kontrol edebilirsiniz.
M Falanga

20

Daktiloyu kullandığınızda: Yukarıdaki çözümlerin çoğu çalışmayabilir veya aşırı karmaşık olabilir.

Durum : Dizeler numara değerleri ile aynı değildir (kasa farklıdır)

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

Sadece kullan:

const color = "green" as Color

15

Ben de aynı derleyici hatasıyla karşılaştım. Sly_cardinal'in yaklaşımının biraz daha kısa bir varyasyonu.

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

Ek olarak: Numaralandırmayı dize olarak serileştiren bir javascript katmanıyla doldurulmuş bir dakikanız varsa (örneğin AngularJS aracılığıyla Asp Web API diyelim) myProp.color = Color[<string><any>myProp.color] Cheers
Victor

1
Bu tanınmış cevap olmalı.
Miroslav Popov

9

TypeScript derleyicisi değişken türünün dize olduğunu bilirse, bu işe yarar:

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

Aksi takdirde, açıkça bir dizeye dönüştürmelisiniz (derleyici uyarılarını önlemek için):

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

Çalışma zamanında her iki çözüm de işe yarayacaktır.


3
neden sadece kalıplaştığı kullanmayan <string>colorNameyerine "" + colorName?
SergeyT

7

Bu soruda çok fazla karışık bilgi var, bu yüzden Nick'in TypeScript'li Modellerde Numaralandırma Kullanma Kılavuzundaki TypeScript 2.x + uygulamasının tamamını ele alalım .

Bu kılavuz: sunucu tarafından istemci tarafında uygun bir Enum olarak modellenecek bir dizi bilinen dizeyi alan istemci tarafı kodu oluşturan kişiler içindir.

Numaralandırmayı tanımla

Enum ile başlayalım. Bunun gibi bir şeye benzemeli:

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

Burada iki nokta var:

  1. Bunları açıkça, birbiriyle ilişkili olmayan bazı sayılarla değil, dizelerle başlatmamızı sağlayan dizgi destekli numaralandırma vakaları olarak ilan ediyoruz.

  2. Biz ya sunucumuz modeli mevcut olmayabilir bir seçenek ekledik: UNKNOWN. Bu, undefinedtercih ettiğiniz gibi ele alınabilir , ancak | undefinedkullanımı kolaylaştırmak için mümkün olduğunca türlerden kaçınmayı seviyorum .

Bir sahip olmak harika bir şey UNKNOWN davaya , kodda gerçekten açık olabilmeniz ve bilinmeyen numaralandırma vakaları için stilleri parlak kırmızı ve göz kırpar hale getirebilmenizdir, böylece bir şeyi doğru şekilde işlemediğinizi bilirsiniz.

Numaralandırmayı ayrıştır

Bu numaralandırmayı başka bir modele gömülü veya tek başına kullanıyor olabilirsiniz, ancak JSON veya XML'den (ha) gelen string-y yazılan numaralandırmayı güçlü yazılan karşılığınıza ayrıştırmanız gerekecektir. Başka bir modele gömüldüğünde, bu ayrıştırıcı sınıf yapıcısında yaşar.

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

  return type;
}

Numaralandırma düzgün ayrıştırılırsa, uygun tür olarak sonuçlanır. Aksi takdirde, olur undefinedve onu kesip UNKNOWNdavanızı iade edebilirsiniz . undefinedBilinmeyen vakanız olarak kullanmayı tercih ederseniz, enum ayrıştırma girişiminden herhangi bir sonucu döndürebilirsiniz.

Oradan, sadece ayrıştırma işlevini kullanma ve yeni güçlü yazılan değişkeninizi kullanma meselesi.

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

6
Ne yazık ki, bu doğru değil veya en azından genelleştirilemez gibi görünüyor. Anahtarlarınız atandıkları dizelere eşit olduğu için çalışır. Benim durumumda olduğu gibi, farklıysalar, bu işe yaramaz. Belgelerin sözleriyle : "String enum üyelerinin hiç ters bir eşleme oluşturmadığını unutmayın." Kodunuz benzer bir şeye derlenecektir IssueType["REPS"]="REPS". REPS="reps"IssueType["REPS"]="reps"
Enumunuzu

... her zaman geri döner IssueType.UNKNOWNçünkü repsnumaralandırmanızda anahtar yoktur . Çok kötü, hala bunun için hiçbir çalışma çözümü bulamadım çünkü dizelerim anahtar olarak kullanılamaz hale getiren tireler içeriyor.
altocumulus

Son olarak, derleyiciyi bunun bir dize sıralaması olmadığına ikna ederek bu cevapta bir çözüm buldum . Bu bilgiyi kendi cevabınızda düzenlemeye değer olabilir.
altocumulus

7

Bir alabilirsiniz bir cevap arıyordu enumbir mesafede string, ancak benim durumumda, çeteleler değerleri farklı dize değerleri meslektaşı vardı. OP için basit bir numaralandırma vardı Color, ama farklı bir şey vardı:

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

Bir dize Gender.CantTellile çözümlemeye çalıştığınızda , orijinal yanıtla "Can't tell"döner undefined.

Başka bir cevap

Temel olarak, bu cevaptan ilham alan başka bir cevap buldum :

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

notlar

  • Biz almak ilk sonucu bir filteristemci enum geçerli bir dize geçiyor varsayarak. Aksi undefinedtakdirde iade edilecektir.
  • Biz döküm enumObjiçin anyçünkü typescript 3.0+ (şu anda typescript 3.5 kullanılarak) ile, enumObjolarak çözümlenir unknown.

Kullanım Örneği

const cantTellStr = "Can't tell";

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

Not: Ve bir yorumda işaret ettiği gibi, ben de kullanmak istedim noImplicitAny.

Güncellenmiş versiyon

Döküm anyve uygun tipler yok.

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

Ayrıca, güncellenmiş sürüm onu ​​çağırmanın daha kolay bir yoluna sahiptir ve daha okunabilir:

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

6

Numaralandırma değerleri (birkaç numaralandırma permutasyon bir sürü test oldu) döngü nasıl bilmek gerekiyordu ve ben iyi çalışması için buldum:

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
}

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


Bu cevap cömert! Sevdim. Özellikle ipin içinden enum yapma şekliniz. Bu, numaralandırmaları veya diğer durumları test ederken çok fazla yazarak tasarruf etmenizi sağlayabilir.
Florian Leitgeb

Evet, bunu Jest's eachile her bir numaralandırma durumunu tek bir yöntemle test etmek için kullanıyorum
mikeb

3

Sıralama

enum MyEnum {
    First,
    Second,
    Three
}

Örnek kullanım

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

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

Büyük / küçük harfe duyarlı ayrıştırmayı yoksay

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

Benim gibi numaralandırmış herkes return enumType[property];enum öğeniz gibi göründüğü bir durumda koymak gerekirSkills = "anyvalue"
neustart47

@ neustart47 lütfen soru sormak ister misiniz?
Очир Дармаев

bu bir soru değil. Aynı davayı arayan herkes için bazı değişikliklerden bahsettim. Cevabınız doğru.
neustart47

2

Yaptığınız şekilde oluşturulan numaralandırmalar, hem ileri hem de (name -> value)geri (value -> name)eşlemeleri depolayan bir nesnede derlenir . Bu krom devtools ekran görüntüsünden gözlemleyebileceğimiz gibi:

resim açıklamasını buraya girin

Çift eşlemenin nasıl çalıştığına ve birinden diğerine nasıl yayınlanacağına dair bir örnek:

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

Bunu dene

var color: Renk = (Herhangi bir renk) ["Yeşil];

3.5.3 sürümü için iyi çalışıyor


0

Numaralandırmanızın işlevselliğini genişletmek için ad alanları kullanıyorsanız, bunun gibi bir şey de yapabilirsiniz.

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

ve böyle kullan

  Color.getInstance('Red');

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.