Typescript härleder inte typer i switch-satsen när generics och villkor används i typdefinitionen
Jag har en typpdefinition för en funktion. Jag definierar typen för det andra argumentet som villkorad på typen av det första argumentet, på följande sätt:
const FOO = "FOO";
const BAR = "BAR";
let fooPayload = {
zip: "zap"
}
let barPayload = {
cat: "dog"
}
type ActionTypes = typeof FOO | typeof BAR;
interface MyFunction<T extends ActionTypes = ActionTypes> {
(
action: {
type: T;
payload: T extends typeof FOO
? typeof fooPayload
: typeof barPayload;
}
): boolean;
}
Funktionen som MyFunction-gränssnittet refererar till har ett switch-uttryck inom sig som byter baserat på action.type och, beroende på fallet, gör något med action.payload. Här är ett exempel:
const myFunction:MyFunction = (action) => {
switch (action.type) {
case FOO:
action.payload.zip = "new zap"
return true
case BAR:
action.payload.cat = "new dog"
return false
default:
return false
}
}
Problemet jag har är att Typescript inte korrekt härleder vad action.payload bör vara från switch-satsen. Till exempel, om action.type är lika med "FOO" bör det härleda att action.payload MÅSTE vara typeof fooPayload. Istället härleder det att det är typeof fooPayload | typeof barPayload.
Detta är inte logiskt eftersom typdefinitionen för action.payload baseras på värdet av action.type, och eftersom action.payload.zip anropas inom en switch-sats kan det endast ske när action.type är lika med "FOO", bör det härledas att den enda möjliga typen för action.payload är typeof fooPayload.
Här är ett kodexempel
Vad gör jag för fel här?
Svar #1
Jag är inte säker på den exakta platsen där detta orsakar problem men i allmänhet är problemet att du försöker återuppfinna vad TypeScript redan gör bra, begränsa type unions.
Villkor är en riktigt intressant funktion men beter sig ibland lite oväntat eftersom de läcker utanför det omfång där de är definierade eftersom de är distributiva.
Jag skulle implementera detta på ett annat sätt:
interface Action<T, P> {
type: T,
payload: P
}
type Actions
= Action<typeof FOO, typeof FooPayload>
| Action<typeof BAR, typeof BarPayload>
;
interface MyFunction {
(action: Actions): boolean;
}
Inga villkor behövs, och TypeScript kommer att avgränsa unionerna som förväntat i switchen.
