typescript - Diskriminerande unionstyper
Varför kan jag inte särskilja den unionen med bara informationen att medlemmen x är en sträng och inte ett nummer? Varför måste jag använda en litteral typ?
type A = { x: string ; y: string }
type B = { x: number ; y: number }
type C = A | B
function fn(p: C) {
if (typeof p.x === 'string') {
// Typescript is unable to infer p as A
// Typescript infer p.y as (string | number), why not just string ?
}
// Typescript is capable of inferring p.x as number - good
// But cannot infer p.y as number, why ?
}
För mig är det inte möjligt att kalla funktionen med en typ som denna {x:string,y:number}, så varför antar TypeScript att detta är möjligt?
Svar #1
Om du kan uttrycka typ i klasser, rekommenderar jag att göra det:
class A { x: string; y: string; }
class B { x: number; y: number }
type C = A | B
function fn(p: C) {
if (p instanceof A) {
// { x: string; y: string; }
}
if (p instanceof B) {
// { x: number; y: number }
}
}
I det här fallet kan du använda instanceof-magik. Det här är det bästa sättet. Annars måste du kontrollera alla egenskaper eftersom nyckelordet type endast lägger till definitioner i källan.
Svar #2
Inte direkt en förklaring till varför Typescript-kompilatorn inte kan skilja en unionstyp med typeof, men handboken säger:
Det finns tre ingredienser:
- Typer som har en gemensam, singleton-typ-egenskap — diskriminanten.
- En typalias som tar unionen av dessa typer — unionen.
- Typvakter på den gemensamma egenskapen.
Så för att kunna avgränsa rätt typ är en singleton-typ egenskap nödvändig. Jag skulle använda anpassade typvakter:
type A = { x: string ; y: string }
type B = { x: number ; y: number }
type C = A | B
function isA(obj: C): obj is A {
return typeof obj.x === 'string';
}
function isB(obj: C): obj is B {
return typeof obj.x === 'number';
}
function fn(p: C) {
if (isA(p)) {
// ...
}
if (isB(p)) {
// ...
}
}
