Varför klagar inte TypeScript när man försöker läsa från never i en unionstyp?


Jag trodde att TypeScript skulle klaga när man försöker läsa egenskapen length av något som kan vara en array eller never, och sedan att jag skulle behöva specificera typen med ett typpredikat. Så är det inte. Varför? Hur skulle jag kunna ändra typerna så att TypeScript tvingar mig att kontrollera att jag inte hanterar ett tomt objekt?

// strictNullChecks = true
type Empty = Record<string, never>;

type NonEmpty = {
  documents: Array<number>;
};

function isEmpty(x: Empty | NonEmpty): x is Empty {
  return !("documents" in x);
}

const count = (x: Empty | NonEmpty) => {
  // TypeScript does not complain about reading `length` property of `never`
  // if (isEmpty(x)) {
  //   return 0;
  // }
  return x.documents.length;
};

Svar #1

Nedan är ett exempel på typförstörelse steg för steg:

type Empty = Record<string, never>;

type NonEmpty = {
  documents: Array<number>;
};

function isEmpty(x: Empty | NonEmpty): x is Empty {
  return !("documents" in x);
}

const count = (x: Empty | NonEmpty) => {

  type D0 = typeof x.documents
  type D1 =  (Empty | NonEmpty)[`documents`]
  type D2 = Empty[`documents`] | NonEmpty[`documents`]
  type D3 =  never | NonEmpty[`documents`]
  type D4 =  NonEmpty[`documents`]
  type D5 =  Array<number>

  return x.documents.length; // x.documents is number[], that's why no error here
};

Och nedan är ett exempel på det förväntade beteendet som kan passa ditt behov:

type EmptyExpected = Record<string, undefined>;

const countExpected = (x: EmptyExpected | NonEmpty) => {

  type D0 = typeof x.documents
  type D1 =  (EmptyExpected | NonEmpty)[`documents`]
  type D2 = EmptyExpected[`documents`] | NonEmpty[`documents`]
  type D3 =  undefined | NonEmpty[`documents`]
  type D4 =  undefined | number[]

  return x.documents.length; // x.documents is number[] | undefined, that's why it is an error here
};

TypeScript-lekplats

Svar #2

Slås aldrig ihop från unionstyper (t.ex. 0 + 5=5).

type Test1 = never | string // string

Never åsidosätter andra typer i intersections-typer. (t.ex. 0 x 5=0)

type Test2 = never & string // never

Dokumentation: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#the-never-type

Relaterade artiklar