The typescript shows an error “Object is possibly ‘undefined'”

This is currently a limitation of TypeScript as described in microsoft/TypeScript#10530.


You’re expecting that by checking feeObj[property] !== undefined that TypeScript will narrow the type of feeObj[property] from number | undefined to undefined, but unfortunately that isn’t happening. TypeScript does normally allow such equality narrowing on property accesses, as shown here:

function works(feeObj: FeeObjType2) {
  const property = "someProp";
  feeObj[property] + 2; // error, possibly undefined
  if (feeObj[property] !== undefined) {
    feeObj[property] + 2; // still error, possibly undefined!
  }
}

That works because property has the string literal type of "someProp". The compiler knows the exact literal value of that key. And so after you check the "someProp" property key of feeObj for undefined, the compiler will realize that it cannot be undefined inside the subsequent code block, and allow you to treat it like a number.

So then why doesn’t the following work?

function doesNotWork(feeObj: FeeObjType2, property: string) {
  // (parameter) property: string
  feeObj[property] + 2; // error, possibly undefined
  if (feeObj[property] !== undefined) {
    feeObj[property] + 2; // still error, possibly undefined!
  }
}

It’s because the type of property is string, which is not a literal type, and currently the narrowing behavior operates only on the types of the property keys, not their identities. From the type system’s perspective, the above code is the same as:

function alsoBad(feeObj: FeeObjType2, property1: string, property2: string) {
  if (feeObj[property1] !== undefined) {
    feeObj[property2] + 2; // error of course
  }
}

Here you’re checking feeObj[property1] and then accessing feeObj[property2]. It makes sense that this wouldn’t be safe, right? You might be thinking “of course that’s not safe, because property1 and property2 might not have the same value, while property obviously has the same value as itself.” But the compiler isn’t tracking identities of the keys, so it doesn’t know that the comparison is to “itself”.

Going through the same exercise with property1 and property2 both being of the same literal type "someProp", you’ll see that it starts working again:

function alsoGood(feeObj: FeeObjType2) {
  const property1 = "someProp";
  // const property1: "someProp"
  const property2 = "someProp";
  // const property2: "someProp"
  if (feeObj[property1] !== undefined) {
    feeObj[property2] + 2; // okay
  }
}

So currently, the only way you can narrow an object’s type by checking its property is if you check a property of known literal type.


The recommended workaround in cases like this is to copy the property to a new variable before checking it. This way you are always narrowing the same variable:

const sumFunc = function (feeObj: FeeObjType2): number {
  let total = 0;
  for (const property in feeObj) {
    if (property) {
      const fp = feeObj[property]; // copy
      if (fp !== undefined) { // check
        total = total + fp; // okay
      }
    }
  }
  return total;
};

Of course in the particular example you don’t need to be so verbose: there’s no real reason to check property for truthiness (if someone wrote feeObj2[""] = 123, would you really want to exclude it from the sum?); you can replace x = x + with the addition assignment operator (+=); you can use the nullish coalescing (??) operator to handle undefined instead of having a separate code block. That gives you:

const sumFunc = function (feeObj: FeeObjType2): number {
  let total = 0;
  for (const property in feeObj) {
    total += feeObj[property] ?? 0; 
  }
  return total;
};

which should behave similarly without needing to rely on narrowing of feeObj[property] at all.

Playground link to code

Leave a Comment