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.
