Hur tvingar jag TypeScript att kontrollera mot alla föreningsmedlemmar?


Så jag har den här koden och den fungerar ganska bra:

interface A {
  type: "a";
  payload: { isDisabled?: boolean };
}

interface B1 {
  type: "b1";
  payload: { children: A[] };
}

interface B2 {
  type: "b2";
  payload: { child: B | null };
}

type B = B1 | B2;

const items: B[] = [
  {
    type: "b2",
    payload: {
      child: {
        type: "b1",
        payload: {
          children: [
            {
              type: "a",
              payload: {
                isDisabled: false,
              },
            },
          ],
        },
      },
    },
  },
];

tsconfig :

{
  "compilerOptions": {
    "rootDir": "./src",
    "outDir": "./lib",
    "strict": true
  },
  "include": ["src/**/*.tsx"]
}

Problemet uppstår när jag (till exempel) ersätter isDisabled: false med isDisabled: 1234. Jag förväntar mig att felet ska vara:

Type 'number' is not assignable to type 'boolean'.

Istället får jag detta:

Type '"b1"' is not assignable to type '"b2"'.

Type '{ children: { type: "a"; payload: { isDisabled: number; }; }[]; }' is not assignable to type '{ child: B | null; }'.
  Object literal may only specify known properties, and 'children' does not exist in type '{ child: B | null; }'.

Det verkar som att TypeScript exkluderar B1 från B och försöker kontrollera items[0].payload.child mot endast B2 (ignorerar type: "b1").

Det finns dock ett par lösningar:

  1. Deklarera B2['payload'] som { child: B }. Att utelämna null åtgärdar det tidigare nämnda beteendet. Tyvärr är detta inte riktigt ett alternativ, eftersom jag vill att child ska kunna vara null.
  2. Ett annat sätt att kringgå problemet är att använda någon annan typ istället för null i B2['payload']['child']. Tyvärr verkar de enda typerna som fungerar där vara tomma tuppler ([]) och tomma objekt (Record<string, never>), och jag skulle verkligen vilja använda ett primitivt värde.
  3. Att sätta strictNullTypes: false i tsconfig fungerar också, men detta är inte lämpligt av uppenbara skäl.
  4. Slutligen är det möjligt att använda typen påstås: const items: B[]=[ ... ] as B[];. Detta verkar överflödigt, det är väldigt lätt att glömma att lägga till det och det är också väldigt svårt att verifiera (till exempel med en linter).

Så, frågan är: är det möjligt att få TypeScript att respektera B1 och rapportera det förväntade felet utan att förlita sig på någon av de tidigare nämnda lösningarna?