Typescript härleder att fältet är sant (kan inte vara undefined), utan användardefinierade typvakter


type Item = {
    left?: { photoSrc: string };
};

type ItemRequired = {
    left: { photoSrc: string };
};

const item: Item = {} as any;

if (item.left) {
    const itemRequired: ItemRequired = item; // fails. Typescript still says left may be undefined
    // I'd like Typescript to know that field left is truthy
}

Jag vet om användardefinierade typvakter. T.ex.

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

Mina frågor:

  1. Varför kan inte Typescript avgöra att left inte kan vara undefined?
  2. Betyder det att jag behöver skapa en typvaktfunktion för varje variant av en typ som jag behöver kontrollera? Finns det inte ett enklare sätt? T.ex. förväntar jag mig att när jag befinner mig inom räckvidden av en if-kontroll som bevisar att left-fältet inte kan vara undefined, förväntar jag mig att Typescript ska veta det.

Det här verkar vara ett väldigt vanligt fall, men jag kunde inte hitta svaret, ursäkta mig om det finns ett.

Svar #1

För att besvara dina frågor:

Jag antar att det är för att Typescript här gör en jämförelse mellan de två typerna: Item och ItemRequired, istället för vad du skulle vilja, vilket är { left:{ photoSrc:string } }.

Typescript kan ofta lista ut vilken del av unionstyper du använder utan typvakter, men det är ännu inte helt perfekt. Det beror på situationen vilka som fungerar och inte fungerar, det finns många möjliga situationer.

Ofta vill människor klara sig utan typkontroller, och det är förståeligt eftersom det ofta verkar som ytterligare, onödig kod. Men det är egentligen utformat för just denna situation:

const itemIsItemRequired = (item: Item): item is ItemRequired =>
  item.left !== undefined;

if (itemIsItemRequired(item)) {
    const itemRequired: ItemRequired = item; // this works
}

Svar #2

För närvarande härleder inte Typescript typer från villkorsstyrd flöde av uttalanden.

const item: Item = {} as any;
if (item.left) {
    const itemRequired: ItemRequired = item; 
}

Som ett resultat smalnar inte ovanstående kodblock faktiskt typen av objekt från Item till ItemRequired. Därför, för kompilatorn, försöker du tilldela två inkompatibla typer - en där vänster är obligatorisk och en annan där den är valfri.

Ett alternativ, som du nämnde, är att du kan skriva en typvakt eller helt enkelt kan du göra följande.

const itemRequired: ItemRequired = item as ItemRequired;

Att skriva typvakter rekommenderas dock alltid.