"Objektet kan være 'undefined'." i TypeScript


Koden nedenfor gir Object is possibly 'undefined'. ved obj.field1 += ',' + v;. TypeScript sier at obj kan være undefined, men obj kan ikke være undefined på dette punktet fordi {field1: 'testtest'} blir tildelt hvis map.get(key) returnerer undefined.

Hvorfor fikk jeg denne feilen? Hvordan kan jeg fikse dette?

interface TestIF {
  field1: string;
}

export class MyClass {
  test(): void {
    const map1: Map<string, TestIF> = new Map();
    const map2: Map<string, string> = new Map();

    const key = 'mapkey';
    let obj = map1.get(key);
    if (obj == null) {
      obj = {field1: 'testtest'};
      map1.set(key, obj);
    }

    map2.forEach( v => {
      obj.field1 += ',' + v;  // Object is possibly 'undefined'.
    });
  }
}

Svar #1

Feilen skjer fordi kontrollflytanalyse er vanskelig (se microsoft/TypeScript#9998), spesielt når informasjonen du vil holde styr på ikke kan representeres i typesystemet.

I det generelle tilfellet kan ikke kompilatoren egentlig finne ut mye om hva som skjer når funksjoner aksepterer callback-funksjoner som endrer variabler. Kontrollflyten inne i slike funksjoner er et fullstendig mysterium; kanskje callback-funksjonen vil bli kalt umiddelbart og nøyaktig én gang. Kanskje callback-funksjonen aldri blir kalt. Kanskje callback-funksjonen blir kalt en million ganger. Eller kanskje den blir kalt asynkront, langt fram i tid. Siden det generelle tilfellet er så håpløst, prøver ikke kompilatoren engang virkelig. Den bruker noen heuristikker som fungerer i mange tilfeller, og som nødvendigvis også feiler i mange tilfeller. Du valgte en av feilene.

Heuristikken som brukes her er at inne i en callback, blir alle innsnevringer som skjedde i det bredere området, tilbakestilt. Det gjør fornuftige ting for kode som dette:

// who knows when this actually calls its callback?
declare function mysteryCallbackCaller(cb: () => void): void;

let a: string | undefined = "hey";
mysteryCallbackCaller(() => a.charAt(0)); // error!  a may be undefined
a = undefined;

Kompilatoren vet ikke når eller om () => a.charAt(0) blir kalt. Hvis den blir kalt umiddelbart når mysteryCallbackCaller() kalles, vil a være definert. Men hvis den blir kalt en gang senere, kan a være udefinert. Siden kompilatoren ikke kan garantere sikkerhet her, rapporterer den en feil.


Så hva kan vi gjøre for å håndtere dette problemet i ditt tilfelle? Det er to hovedløsninger jeg kan tenke på. Den ene er å bare fortelle kompilatoren at den tar feil, og at du er sikker på at obj vil være definert. Dette kan gjøres ved å bruke ! ikke-null påstandsoperator:

map2.forEach(v => {
  obj!.field1 += "," + v; // okay now
});

Dette fungerer uten noen kompileringstid-feil. Forbeholdet med denne løsningen er at ansvaret for å sikre at obj er definert nå kun er ditt og ikke kompilatorens. Hvis du endrer den foregående koden og obj faktisk kan være udefinert, vil typepåstanden fortsatt undertrykke feilen, og du vil få problemer under kjøring.


Den andre løsningen er å endre det du gjør slik at kompilatoren kan verifisere at tilbakeringingen din er trygg. Den enkleste måten å gjøre det på er å bruke en ny variabel:

// over here the compiler knows obj is defined
const constObj = obj; // type is inferred as TestIF
map2.forEach(v => {
  constObj.field1 += "," + v; // okay, constObj is TestIF, so this works
});

Alt jeg har gjort her er å tilordne obj til constObj. Men på tidspunktet denne tilordningen skjer, kan ikke obj være undefined. Dermed er constObj bare en TestIF, og ikke en TestIF | undefined. Og siden constObj aldri blir tilordnet på nytt og ikke kan være undefined, fungerer resten av koden.


Lenke til kode

Relaterte artikler