TypeScript-fel vid hantering av en okänd variabel: Egenskapen finns inte på typen 'object'
Jag försöker förstå hur man korrekt hanterar en okänd variabel, som kan vara ett objekt med vissa egenskaper jag kan använda. Detta kommer från en mängd olika try/catch-scenarier när man arbetar med ett tredjepartsbibliotek som fortfarande är under utveckling, så "e" kan eller kanske inte är ett objekt med vissa egenskaper.
Nedan är en förenklad version av min fråga, men det finns andra manifestationer av problemet. Därför önskar jag undvika typförklaringar eller inaktivera typkontroll. Det jag söker är ett korrekt sätt att kontrollera ett "okänt" värde för att se om det är ett objekt med vissa egenskaper.
Varför är följande if-sats otillräcklig? TypeScript ser det bara som ett okänt objekt och använder inte den information jag har lämnat för att säkerställa att (1) det är ett objekt, (2) det är inte null, och (3) det har nyckeln "errNum".
Vilket annat möjligt tillstånd skulle kunna orsaka att "console.log(e.errNum)" kastar ett fel vid körning efter alla dessa kontroller? Detta misslyckas både i VSCode GUI och när man bygger med webpack 5 (med användning av eslint efter nyligen migration från tslint). Jag kan inte komma på några andra ytterlighetsfall som skulle orsaka problem vid körning.
try {
const err = { errNum: 12 };
throw err;
} catch (e) {
// e is inferred as unknown, as it should be
if (e && typeof e === 'object' && 'errNum' in e) console.log(e.errNum); // TS error: Property 'errNum' does not exist on type 'object'.
}
Svar #1
Du kommer inte att kunna dra nytta av typkontrollantens inferenskapacitet eftersom catch-satsens variabel är okänd. I detta fall kan det vara enklare att använda villkoret för att avgöra att errNum är en egenskap i en typvaktfunktion.
catch (e)
{
if (isErrorNumObj(e)) console.log(e.errNum);
}
function isErrorNumObj<T>(obj: T): obj is T & { errNum: unknown } {
return obj && 'errNum' in obj;
}
Du kan till och med göra en mer generell version som kan acceptera ett valfritt antal props.
try {
const err = { errNum: 12, stackTrace: 'abc' };
throw err;
} catch (e: unknown) {
if (hasProps(e, 'errNum', 'stackTrace')) {
console.log(e.errNum, e.stackTrace);
}
}
function hasProps<T, U extends string | number | symbol>(obj: T, ...propName: U[]): obj is T & { [P in U]: unknown } {
return !!obj // ensure not nullish.
&& (typeof obj == "object" || typeof obj == "function") // ensure not primitive.
&& propName.every(x => x in obj); // ensure every prop is in obj.
}
Svar #2
Jag skulle skapa en användardefinierad typ-predikat.
function isErrNum(x: any): x is { errNum: number } {
return !!x && 'errNum' in x;
}
try {
const err = { errNum: 12 };
throw err;
} catch (e: unknown) {
// e is inferred as unknown, as it should be
if (isErrNum(e)) {
console.log(e.errNum);
}
}
Vi extraherar vår "test"-kod till en funktion som returnerar "variabel är typ". Och ett "true"-returvärde informerar sedan kompilatorn om att i kontexten att detta är sant, är objektet av den deklarerade typen och är inte den typen i ett else.
TypeScript-lekplats
