Typescript - Incorrectly inferring 'never' Typescript - Incorrectly inferring 'never' typescript typescript

Typescript - Incorrectly inferring 'never'


If you are absolutely sure that a has a value there, than you can put the ! after the variable

let a: number | null = null;[1].forEach(() => {  a = 1;});if (a !== null)  a!.toFixed(); //

I would not use null thought but undefined, so no need to use !

let a: number | undefined;[1].forEach(() => {  a = 1;});if (a) {  a.toFixed(); // No problem here}

Also as recommendation use !== not !=


Late to the party, but here are my 2 cents.

Remark to the accepted answer

if (a) {  a.toFixed(); // No problem here}

note, that the if block will not be invoked when a is 0.

  • to fix this, use if (a !== undefined)
  • otherwise (when you really don't want to handle 0 you are better off initializing a to 0 like so:
    let a = 0; // typescript will infer the type number    ...    if (a) {      // a is of type number and !== 0    }

Answer to a comment

Why would you initialize a variable using undefined?

People sometimes do this, because some tools (IDE, linters, ..) report errors/warnings otherwise.

e.g. here is a warning when you use IntelliJ IDEA with the default typescript settings:
enter image description here

I recommend to deactivate these checks, since an uninitialized variable in javascript always has the value undefined: i.e. in some other languages (i.e. C), the variable might have some random "garbage" value.

Quote from MDN: Global_Objects/undefined#description

A variable that has not been assigned a value is of type undefined.

And for all other values (i.e. values that are not undefined), the typescript compiler will show an error:
TS2454: Variable 'xxx' is used before being assigned.

Answer to the original question

let a: number | null = null;[1].forEach(() => {  a = 1;});if (a != null)  a.toFixed(); // Error: Property 'toFixed' does not exist on type 'never'.

This only happens when the compiler option strictNullChecks is on.

This quote describes the reason well (Quote Reference)

While strictNullChecks implies that it is just checking for usage of variables that might be undefined or null it really turns the compiler into a very pessimistic mode, where when there are no contextual way of inferring the type, it will choose the narrowest type, instead of the widest type,

This means, in detail:

  • since the typescript compiler is not smart enough to know if the forEach loop is invoked (and thus a value is assigned), it takes the pessimistic approach and assumes that x remains null
  • thus, the type of x after the loop is null (not number | null as we might expect)
  • now, the final if block checks if x !=== null which can never be the case (since typescript assumes that x is null when the if-statement is executed. Thus the type of x inside of the if-statement is never
  • so one "solution" is to explicitly tell typescript that you are sure, that the value of x is defined by using x!.toFixed()

Misc

strictNullChecks

When strictNullChecks is off, the code works: TypeScript example: strictNullChecks=off
I strongly recommend NOT to do that.

for..of loop

When you use a for..of loop instead of forEach() the code works, even when strictNullChecks is on: Playground

let a: number | null = null;for (const i of [1]) {  a = 1;};if (a != null)  a.toFixed();

Other init values

You can also consider other initialization values (instead of undefined or null): Playground

let a = 0; // typescript will infer that a is of type number[1].forEach(() => {  a = 1;});if (a >= 0)  a.toFixed();let b = NaN; // typescript will infer that b is of type number[1].forEach(() => {  a = 1;});if (!isNaN(b))  b.toFixed();