typescript - Inferensfeil ved bruk av negert sann/usann innsnevring


Når jeg bruker TypeScript i VS Code, klarer jeg ikke å utlede en type fra en union av objekter ved å bruke negert sann/falsk innsnevring.

Ta følgende eksempel:

// object union type
export type Result = { ok: true; value: 'foo' } | { ok: false; error: 'bar' };

const resolve = (): Result => ({ ok: false, error: 'bar'});
const result = resolve();

// narrowing on my machine works fine without negation
if (result.ok) console.log(result.value)

// narrowing with negation causes an error
if (!result.ok) console.log(result.error)

Feilen jeg får er: Property 'error' does not exist on type '{ ok: true; value: void; }' - noe som antyder at unionstypen ikke blir innsnevret av if-setningen og at ingen inferens skjer.

Dette er uvanlig fordi i andre miljøer fungerer denne typen innsnevring, slik som i dette sandkassemiljøet hvor negert sann/falsk-innsnevring fungerer fint.

Så problemet må være miljøet mitt, men hva i miljøet mitt kan forårsake at TypeScript oppfører seg unormalt på denne måten? Og mer spesifikt - hvordan kan jeg fikse TypeScript-snevringen min?

Detaljer om mitt miljø:

  • Det er et NX-prosjekt, som bruker "typescript": "4.7.4"
  • VS Code settings.json: "typescript.tsdk": "./node_modules/typescript/lib"
  • maskin: macOS, 2,3 GHz 8-kjerners Intel i9

Svar #1

Uten mer utførlig informasjon om ditt IDE-miljø, kan jeg bare gjette. Jeg tror imidlertid at du kan løse dette problemet med eiendomstilgjengelighet samtidig som du opprettholder typesikkerhet uten å gjøre endringer der.

Når man håndterer en diskriminert union, er den typiske tilnærmingen det du viste: A | B. Men en annen måte å nærme seg det på er å inkludere alle egenskaper fra begge typene i hver type, og å sette de unike egenskapene til hver type som valgfrie og never på den andre.

Her er et eksempel på hvordan man gjør det ved å bruke et par typeverktøy:

TS-lekeplass

type ANotB<A, B> = A & Partial<Record<Exclude<keyof B, keyof A>, never>>;
type AOrB<A, B> = ANotB<A, B> | ANotB<B, A>;

type OKResult = { ok: true; value: 'foo' };
type NOKResult = { ok: false; error: 'bar' };
type Result = AOrB<OKResult, NOKResult>;

declare const result: Result;

if (result.ok) {
  result.ok;
       //^? (property) ok: true
  result.error;
       //^? (property) error?: undefined
  result.value;
       //^? (property) value: "foo"
}

if (!result.ok) {
  result.ok;
       //^? (property) ok: false
  result.error;
       //^? (property) error: "bar"
  result.value;
       //^? (property) value?: undefined
}

Ved å bruke denne tilnærmingen, forhindrer ikke TypeScript deg fra å få tilgang til egenskaper ved hjelp av punktnotasjon som kanskje bare finnes i den andre unionstypen. Når man bruker den tradisjonelle tilnærmingen, må man først bruke in-operatoren for å informere kompilatoren om at egenskapen eksisterer.

Relaterte artikler