This is a basic use-case: Initializing a variable with null then changing the value in some nested loop/function:
let a: number | null = null;
[1].forEach(() => {
a = 1;
});
if (a != null)
a.toFixed(); // Error: Property 'toFixed' does not exist on type 'never'.
However typescript infers a
's type to be never
. I would assume that without the if
it would assume it to be null | number
in which case I could get an error stating the property does not exist on null, but why is it assuming it to be never based on just the initial assignment value.
Am I doing something wrong?
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) { // <-- if undefined or 0
a.toFixed(); // No problem here
}
Also as recommendation use !==
not !=
Late to the party, but here are my 2 cents.
if (a) {
a.toFixed(); // No problem here
}
note, that the if block will not be invoked when a
is 0
.
if (a !== undefined)
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
}
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.
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:
x
remains null
x
after the loop is null
(not number | null
as we might expect)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
x
is defined by using x!.toFixed()
When strictNullChecks
is off, the code works: TypeScript example: strictNullChecks=off
I strongly recommend NOT to do that.
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();
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With