Typescript: Hur skriver man en typvakt för att avgöra om ett värde är en medlem av en union?
Jag har en typ definierad på detta sätt:
export type Thing = "thinga" | "thingb"
Finns det ett sätt för mig att avgöra om ett godtyckligt strängvärde som jag har är en del av den unionen?
let value1 = "thinga";
let value2 = "otherthing";
console.log("value1: " + value1 <is in> Thing );
console.log("value2: " + value2 <is in> Thing );
Där jag vill att det ska vara sant för value1, men falskt för value2. Det faktiska användningsfallet är att ha en array av strängar som jag vill filtrera bort om de inte finns i definitionen (och eventuellt logga) och sedan konvertera till korrekt typ.
Problemet är att Thing-typen är genererad kod som kompileras in med min kod och värdena kan ändras - så jag vill inte skriva villkorslogik i en typkontroll hårdkodad till värdet. Jag styr inte det som genererar koden - annars antar jag att det i det här fallet skulle vara enkelt att göra det jag vill om typen istället var ett strängenum.
Jag hoppas att jag kan skriva någon slags typkontroll som använder keyof eller något liknande? Men jag kan inte se hur man får det att fungera.
Typescript-versionen är 2.8.1, även om uppgradering inte är ett stort problem.
Jag är medveten om denna fråga: https://stackoverflow.com/a/50085718/924597, men jag tror inte att detta är en dubbel eftersom jag inte kan använda svaret på den frågan.
Svar #1
Det finns inget sätt vid körning att komma åt värdena i unionen av strängliteraltyper eftersom all typinformation tas bort vid kompilering. Vi kan konstruera en array med värdena på ett sätt som kommer att orsaka ett kompileringstidfel om unionstypen ändras.
export type Thing = "thinga" | "thingb"
function getAllValues<T>() {
class Helper<TOriginal, TLeft extends TOriginal> {
private data: TOriginal[] = [] ;
private dummy!: TLeft;
done<TThis extends Helper<any, never>>(this: TThis) : TOriginal[]{
return this.data;
}
push<TValue extends TLeft>(value: TValue) : Helper<TOriginal, Exclude<TLeft, TValue>> {
this.data.push(value);
return this as any;
}
}
return new Helper<T, T>();
}
let allValues = getAllValues<Thing>().push("thinga").push("thingb").done(); //Ok
let oldValues = getAllValues<Thing>().push("thinga").push("thingb").push("oldThing").done(); // error oldThinig is not part of the union
let missingValues = getAllValues<Thing>().push("thinga").done(); // error we are missing a value
Nu när vi har alla värden är det enkelt att skapa en typvakt:
const isThing = (function(){
let allValues = getAllValues<Thing>().push("thinga").push("thingb").done();
return function(s: string) : s is Thing {
return allValues.indexOf(s as Thing) != -1;
}
})()
let value1 = "thinga";
let value2 = "otherthing";
console.log("value1: " + isThing(value1) );
console.log("value2: " + isThing(value2) );
