typescript - härleda en unionstyp från en funktions parametrar
I Typescript, när jag kontrollerar en variabel, härleder då-grenen variabeltypen baserat på villkoret:
type MyType = 'val1' | 'val2' | 'val3'
const variable = 'val1' as MyType
if (variable === 'val2' || variable === 'val3') {
// typeof variable == 'val2' | 'val3'
}
Koden ovan är pratig särskilt när jag kontrollerar för mer än två värden och jag vill ersätta den med
if (hasOneOf(variable, 'val2', 'val3')) {
// typeof variable == 'val2' | 'val3'
}
Lösning med Jag är delvis nöjd är:
const hasOneOf = <T, U extends T>(value: T | undefined, ...values: U[]): value is U => value !== undefined && values.indexOf(value as U) >= 0
men det fungerar bara när jag uttryckligen definierar typer
if (hasOneOf(variable, 'val2', 'val3')) {
// typeof variable == MyType
}
// vs
if (hasOneOf<MyType, 'val2' | 'val3'>(variable, 'val2', 'val3')) {
// typeof variable == 'val2' | 'val3'
}
Är det möjligt att skapa en funktion hasOneOf som härleder generiska typer och returnerar en union definierad av parametrarna ('val2', 'val3' => 'val2' | 'val3')?
Svar #1
Om du letar efter en lösning som accepterar ett variabelt antal värden för unionen, är nedan en variation på det som finns i din fråga. Du kan skicka en readonly-array som det andra argumentet och kompilatorn kommer att skapa en union från typerna av dess medlemmar. (Jag tror att tillägget av arrayklamrarna och as const-påståendet fortfarande är mer kortfattat än att ange typargument).
TS Playground-länk
function isInUnion <T, U extends T>(value: T, values: readonly U[]): value is U {
return values.indexOf(value as U) >= 0;
}
let str = 'val1'; // string
if (isInUnion(str, ['val2', 'val3'] as const)) {
str // "val2" | "val3"
}
let num = 1; // number
if (isInUnion(num, [2, 3] as const)) {
num // 2 | 3
}
Original svar:
Du kan använda ett typ-predikat för detta:
TS Playground-länk
function isInUnion <S1 extends string, S2 extends string>(
value: string,
str1: S1,
str2: S2,
): value is S1 | S2 {
return value === str1 || value === str2;
}
let str = 'val1'; // string
if (isInUnion(str, 'val2', 'val3')) {
// in this block, str is "val2" | "val3"
}
