TypeScript'te bir string enum değerlerinin sorgulanması için doğru tipler


Diyelim ki bu dize enum'ım var:

enum Color {
    None = "Q",
    Red = "R",
    Green = "G",
    Blue = "B",
}

Şimdi, TypeScript'te string enum'ların ters eşlemeleri yok, bu yüzden olası bir Color elde etmek için Color["Q"] yazamıyorum. Kendi yardımcı fonksiyonumu tanımlamayı denedim:

type StringEnum = {[key: string]: string};
function lookup<E extends StringEnum>(stringEnum: E, s: string): keyof E | undefined {
    for (const enumValue of keysOf(stringEnum)) {
        if (stringEnum[enumValue] === s) {
            return enumValue;
        }
    }
    return undefined;
}

keysOf ile

function keysOf<K extends {}>(o: K): (keyof K)[];
function keysOf(o: any) { return Object.keys(o); }

… ama aslında, dönüş tipinin ne olması gerektiğini bilmiyorum. Bu derlenmesi amaçlanmış ama derlenmiyor:

const color: Color = lookup(Color, "Q") || Color.None;

çünkü lookup fonksiyonunun dönüş tipi artık sadece Color | undefined yerine "None" | "Red" | "Green" | "Blue" | undefined.

Tür bilgisi korunurken bu iyi bir şekilde çözülebilir mi?

Cevap #1

lookup fonksiyonunuz bir Enum string anahtarı döndürüyor, ancak lookup undefined dönerse kısa devre olarak Enum değeri Color.None da sağlıyorsunuz. Yani burada türler eşleşmiyor.

Genel olarak, string Enum anahtarları bu şekilde yazılabilir:

type ColorKeys = keyof typeof Color // "None" | "Red" | "Green" | "Blue"
const blueKey: keyof typeof Color = "Blue" 

Eğer const blueValue=Color.None yazarsanız, blueValue değişkeni Enum string değeri "Q"'yu tutar, literal "None"'u değil. Bu nedenle const color atamasını şu şekilde düzeltebiliriz:

const color = lookup(Color, "Q") || "None";
// or with explicit type
const color: keyof typeof Color = lookup(Color, "Q") || "None";

Enum türlerinin bir indeks imzası olamaz, bu yüzden derlemesi için burada StringEnum türünü {[key: string]: any} olarak yazdım.

Oyun alanı

Güncelleme: lookup ile yazılmış enum değerini alın

Eğer lookup fonksiyonuna daraltılmamış bir string değeri geçmek ve mevcutsa onu daraltılmış enum değeri türü olarak geri almak (aksi takdirde undefined) istiyorsanız, bunu ilk örneğinizle benzer bir şekilde yapabilirsiniz:

function lookup<E extends StringEnum>(
  stringEnum: E,
  s: string
): E[keyof E] | undefined {
  for (const enumKey of keysOf(stringEnum)) {
    if (stringEnum[enumKey] === s) {
      // here we have to help the compiler
      return stringEnum[enumKey] as E[keyof E];
    }
  }
  return undefined;
}

// let's test it
const look = lookup(Color, "Q") // const look: Color | undefined

// for further understanding
type AllColorKeys = keyof typeof Color // = "None" | "Red" | "Green" | "Blue"
type AllColorValues = (typeof Color)[keyof typeof Color] // = Color
type IsColorSuperType = Color.Blue extends Color ? true: false // = true

Color enum durumu söz konusu olduğunda, döndürülen arama türü size Color | undefined türünde Q değerini verir; çünkü tüm olası Color enum değerleri Color.None, Color.Red vb.'nin birleşimi, üst tür olarak Color türünde olur. Tanımlı enum yapısının kendisi ise bir sınıfın statik ve örnek tarafıyla karşılaştırılabilir şekilde typeof Color türüne sahiptir.

Oyun alanı

İlgili makaleler