Nøyaktige typer for et oppslag av verdier fra en streng-enum i TypeScript


Anta at jeg har denne strengenum:

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

Nå, i TypeScript har string-enumerasjoner ingen omvendte kartlegginger, så jeg kan ikke skrive Color["Q"] for å få tilbake en potensiell Color. Jeg prøvde å erklære min egen hjelpefunksjon:

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

med keysOf som

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

… men egentlig vet jeg ikke hva returtypen skal være. Dette er ment å kompilere, men gjør det ikke:

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

fordi returtypen til lookup-funksjonen nå er "None" | "Red" | "Green" | "Blue" | undefined i stedet for bare Color | undefined.

Kan dette løses godt samtidig som typeinformasjonen bevares?

Svar #1

Din lookup-funksjon returnerer en Enum-streng key, men du gir også Enum-value Color.None som en snarvei i tilfelle lookup returnerer undefined. Så typene stemmer ikke her.

Generelt kan streng-Enum-nøkler typiseres slik:

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

Hvis du skriver const blueValue=Color.None, holder variabelen blueValue Enum-strengverdien "Q", ikke den bokstavelige "None". Så vi kan fikse const color-tilordningen på følgende måte:

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

Enum-typer kan ikke ha en indeks-signatur, så jeg typet StringEnum som {[key: string]: any} her for å få det til å kompilere.

Lekeplass

Oppdatering: få skrevet enum-verdi fra lookup

Hvis du vil sende inn en unødvendig begrenset strengverdi til lookup og gi den tilbake som en begrenset enum-verditype hvis den eksisterer (ellers undefined), kan du gjøre det på en lignende måte som i ditt første eksempel:

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

I tilfelle av Color enum, vil den returnerte oppslagstypen gi deg tilbake verdien Q med typen Color | undefined, ettersom foreningen av alle mulige Color enum-verdier Color.None, Color.Red og så videre vil være av supertypen Color. Den definerte enum-konstruksjonen selv har typen typeof Color, sammenlignbar med den statiske og instanssiden av en klasse.

Lekeplass

Relaterte artikler