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 initializinga
to0
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:
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
remainsnull
- thus, the type of
x
after the loop isnull
(notnumber | 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 isnull
when the if-statement is executed. Thus the type ofx
inside of the if-statement isnever
- so one "solution" is to explicitly tell typescript that you are sure, that the value of
x
is defined by usingx!.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();