typescript - Typavsmalningsfel om diskriminant-egenskapen är union


Jag skrev något som följande kod i TypeScript. Typen ABCD är som ett tillståndsobjekt och dess kind-egenskap representerar typen av tillståndet. Eftersom några av dem (kind är 'a' eller 'b') har en gemensam extra egenskap extra, skrev jag en gemensam typ av dem som AB. De andra har ingen extra egenskap, så jag slog ihop dem som CD enligt nedan.

type AB = {
  kind: 'a' | 'b',
  extra: string
};

type CD = {
  kind: 'c' | 'd'
};

type ABCD = AB | CD;

function printExtra(x: ABCD) {
  if (x.kind === 'c' || x.kind === 'd') return;
  console.log(x.extra);
}

Funktionen printExtra skriver ut extra-egenskapen om kind varken är 'c' eller 'd'. Jag var övertygad om att kompileringen skulle gå igenom eftersom det if-uttalandet bekräftar att x inte är CD och eftersom AB är den enda kandidaten för typen, är det uppenbart att x är AB. Men TypeScript-kompilatorn misslyckades med att kompilera med följande meddelande.

Egenskapen 'extra' finns inte på typen 'ABCD'. Egenskapen 'extra' finns inte på typen 'CD'.

Varför misslyckades ovanstående kod att kompilera? Jag har också provat följande kod och bekräftat att den kompileras framgångsrikt, men om möjligt vill jag inte använda den eftersom den är något redundant i mitt projekt.

type A = { kind: 'a', extra: string };
type B = { kind: 'b', extra: string };
type C = { kind: 'c' };
type D = { kind: 'd' };

type ABCD = A | B | C | D;

Svar #1

Du skulle kunna omstrukturera din kod för att kontrollera åt andra hållet

 function printExtra(x: ABCD) {
    if (x.kind === 'a' || x.kind === 'b') {
        console.log(x.extra); // no error
    }
  }

Svar #2

Tar din logik ett steg längre:

type A = { kind: 'a', extra: string };
type B = { kind: 'b', extra: string };
type C = { kind: 'c' };
type D = { kind: 'd' };

type AB = A | B;
type CD = C | D;

type ABCD = AB | CD;

Så då skulle detta också fungera:

type AB = ({ kind: 'a' } | { kind: 'b' }) & { extra: string };
type CD = { kind: 'c' } | { kind: 'd' };

type ABCD = AB | CD;

Låt oss göra detta lite mindre ordrikt:

type Kind<T extends string> = { [K in T]: { kind: K } }[T];

type AB = Kind<'a' | 'b'> & { extra: string };
type CD = Kind<'c' | 'd'>;

type ABCD = AB | CD;

TS Lekplats