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
