Ahmad
Ahmad

Reputation: 5760

Typescript "Object is possibly 'null'" false alarm

I have the following code:

isEmpty(vehicle: VehicleTitle) {
    return this.options && this.options[vehicle.type] && this.options[vehicle.type].length === 0;
}

The IDE (VSCode) points to this.options[vehicle.type].length and says

Object is possibly 'null'

Don't you think this is a false alarm? What is wrong here?

EDIT

Minimal reproducible example here.

Upvotes: 1

Views: 216

Answers (1)

VLAZ
VLAZ

Reputation: 29087

This seems like a bug in TypeScript!

Here is a previous bug along these lines That bug was fixed in July 2019 but the behaviour that was affected was:

const obj: { [key: string]: any[] | null }  = { prop: [] };

obj["prop"] && obj["prop"].length; //error
obj.prop && obj.prop.length; //OK

Since version 3.6.0 that's no longer the case and both lines are accepted. It seems that now there is a ever so slightly different issue but the outline is the same:

const obj: { [key: string]: any[] | null }  = { prop: [] };

const key = "prop" as const;
obj[key] && obj[key].length; //error
obj.prop && obj.prop.length; //OK

When using any variable, even a constant, to do the property access then TypeScript reports the same error as before.

There is a similar older bug that is to do with bracket notation in general not being treated the same as dot notation. That one had been closed in August 2018 but has been reopened 12 days ago (Match 2020). It appears like this the team is aware of this problem and are revisiting the latter issue for a fix.

In the meantime, here are few ways to avoid the compilation error:

Use a non-null assertion

You can simply do a non-null assertion using the postfix ! operator and you will correct the problem:

isEmpty(vehicle: VehicleTitle) {
  return this.options && this.options[vehicle.type] && this.options[vehicle.type]!.length === 0;
//                                                                               ^
}

Playground Link

Use an intermediate variable for the check

If you want to avoid the assertion, you can just fetch the value and then use it. That will assure the compiler that you aren't actually fetching two different values:

isEmpty(vehicle: VehicleTitle) {
  const value = this.options && this.options[vehicle.type];
  return value && value.length === 0;
}

Playground Link

Use optional chaining

Typescript also has a feature that will do the same check for you, called optional chaining. It's a postfix ? after each property that might not exist:

isEmpty(vehicle: VehicleTitle) {
  return this.options?.[vehicle.type]?.length === 0;
//                   ^               ^
}

Playground Link

Upvotes: 3

Related Questions