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

Relaterade artiklar