let a = [];
a.push(0);
The code above compiles fine when
noImplicitAny
and strictNullChecks
are set to truenoImplicitAny
and strictNullChecks
are set to falseBut it issues an error on the second line when
noImplicitAny
is false and strictNullChecks
is trueThe error issued is Argument of type 'number' is not assignable to parameter of type 'never'.
It seems that in the first two cases a
is being inferred as a any[]
, and in the last case it is inferred as a never[]
, but why does this happen?
If noImplicitAny
is false, shouldn't it be able to still infer it as any[]
? Conversely, why is it being inferred as any[]
in the first case where noImplicitAny
is true? It seems that the expected behaviors are reversed somehow...
Check the images attached below for the errors and options used.
First two cases:
Last case:
This issue was raised in microsoft/TypeScript#36987. The authoritative (but not particularly informative) answer there is that this behavior is as intended, and required for backward compatibility. Generally speaking TypeScript doesn't introduce breaking changes unless these changes improve many more things than they break.
When the --noImplicitAny
compiler option and the --strictNullChecks
compiler option are both enabled, then empty arrays are not really of type any[]
; instead they are "auto" or "wildcard" or "evolving" types that change depending on what values are observed to be added to them. This is implemented in microsoft/TypeScript#11432:
let a = []; // auto-typed
a.push(0); // now a is seen as number[]
a.map(x => x.toFixed()) // okay
a.map(x => x.toUpperCase()) // error, numbers don't have a toUpperCase() method
When --noImplicitAny
is off, this doesn't happen, since doing so would have added new errors to existing code which used to be "fine" (if a
is of type any[]
, then x
in the map()
method should be of type any
, and so no error should occur on x.toUpperCase()
or typos like x.toFaxed()
or anything).
As for the difference between --strictNullChecks
being on or off, there were several bug fixes having to do with empty array literals interacting badly with other use cases; see microsoft/TypeScript#19576 and microsoft/TypeScript#19745. These fixes changed the behavior when --strictNullChecks
is on, but not when it is off, again to preserve backward compatibility in these other use cases.
So that's what's going on, more or less. You're seeing the consequences of several features and bug fixes interacting in a peculiar way in the face of certain compiler options.
Pragmatically speaking you should always use the full --strict
suite of compiler options where possible. This gives you a "standard" amount of type safety and is used widely so there's a large amount of documentation and discussion in the community. If you selectively disable some compiler options and you run into something strange or unexpected, there are fewer resources available to you, because you might be among very few people with such a configuration. And even if you find a genuine language bug, there will be less pressure for it to be fixed, so it might persist for a long time or forever.
Playground link to code
This is intended behaviour as it can be observed here.
It has mostly to do with backwards compatibility. The main gist is something along these lines
undefined[]
that gets widened to any[]
strictNullChecks
is true then undefined
is not an option so it gets updated to never[]
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