let a = [];
a.push(0);
The code above compiles fine when
- both
noImplicitAny
andstrictNullChecks
are set to true - both
noImplicitAny
andstrictNullChecks
are set to false
But it issues an error on the second line when
noImplicitAny
is false andstrictNullChecks
is true
The 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.
CodePudding user response:
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 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.
CodePudding user response:
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
- the type for the array is actually
undefined[]
that gets widened toany[]
- but when
strictNullChecks
is true thenundefined
is not an option so it gets updated tonever[]