Varför visar TypeScript ett fel i detta scenario med en unionstyp?
I detta scenario:
type Type = {
a: string;
b: string;
} | {
a: number;
b: number;
};
const func = ({a, b}: Type): string => {
if (typeof a === 'string') {
return b;
}
return 'w/e';
}
func({a: 1, b: 'w/e'});
Jag får ett fel på return b; som säger
Typen 'string | number' kan inte tilldelas typen 'string'.
Typen 'number' kan inte tilldelas typen 'string'.
Från vad jag förstår av felet, tolkar Typescript typen Type som
type Type = {
a: string | number;
b: string | number;
};
, men även om det är fallet, varför kan jag inte kalla func-funktionen med en sträng och ett nummer?
Felet vid samtalet anger
Argument av typen '{ a: number; b: string; }' kan inte tilldelas till parametern av typen 'Type'.
Typen '{ a: number; b: string; }' kan inte tilldelas typen '{ a: number; b: number; }'.
Typer av egendom 'b' är inkompatibla.
Typen 'string' kan inte tilldelas typen 'number'.
(ts version: 4.7.2)
Lekplatslänk
- Redigera efter UnholySheep:s kommentar:
Funktionsanropet med fel typer var bara för att testa min hypotes om varför jag får det föregående felet. Varför jag får ett fel vid return b; är något jag inte förstår.
Svar #1
Okej, svaret är Discriminated Unions. Jag måste använda en diskriminant, en gemensam egenskap i varje element i unionen. Den egenskapen, diskriminanten:
- Måste vara en primitiv typ, och
- Måste ha ett specifikt värde och inte typen av det värdet. t.ex.
isPrimary: true, inteisPrimary: boolean.
Då kan jag kontrollera den egenskapens värde för att anta de andra egenskapernas typer.
Också, i Typescript-versioner < 4.6, om jag har ett objekt med en typ specificerad som ovan, om jag destrukturerar det, betraktas de nya variablerna som oberoende och jag kommer fortsätta att få ett fel som liknar det ursprungliga exemplet. Destrukturering fungerar i version 4.6 eller högre.
Lekplats exempel (version 4.6)
Lekplats exempel (version 4.5)
Användbara länkar:
- @Austaras länk till Microsofts inlägg som tillkännager stöd för destrukturering
- Förståelse av diskriminerade unioner
- Typescripts singleton-typer
Tack @Austaras och @coglialoro
Svar #2
TS kan inte spåra relationen mellan a och b efter att du destruerar dem, så de båda kommer att härledas som string | number. TS 4.6 introducerade en begränsad kontrollflödesanalys för destrukturering, som endast gäller för diskriminerande unioner.