Description
π Search Terms
"evolving any", "non-null assertion", "compiler api"
π Version & Regression Information
- This changed in version 5.1
β― Playground Link
NOTE: I linked to the typescript-eslint playground just because it both supports // ^?
and also provides access to the TS types so you can easily see the issue. You can ofc reproduce this in the TS playground too.
π» Code
declare function foo(): number | undefined
declare function bar(): number
function main1() {
let x;
x = bar();
x = foo();
//^?
let y1 =
// ^?
x;
// ^?
let y2 =
// ^?
x!;
// ^?
let y3 =
// ^?
x as number;
// ^?
}
π Actual behavior
declare function foo(): number | undefined
declare function bar(): number
function main1() {
let x;
x = bar();
x = foo();
//^? any β
let y1 =
// ^? number | undefined β
x;
// ^? number | undefined β
let y2 =
// ^? number β
x!;
// ^? number β
let y3 =
// ^? number β
x as number;
// ^? number | undefined β
}
π Expected behavior
declare function foo(): number | undefined
declare function bar(): number
function main1() {
let x;
x = bar();
x = foo();
//^? any β
let y1 =
// ^? number | undefined β
x;
// ^? number | undefined β
let y2 =
// ^? number β
x!;
// ^? number | undefined ββββ
let y3 =
// ^? number β
x as number;
// ^? number | undefined β
}
Additional information about the issue
Reference: typescript-eslint/typescript-eslint#10334
TypeScript-ESLint has a rule called no-unnecessary-type-assertion
which, as the name implies, flags unnecessary type assertions to help keep the code clean.
One of the checks done by the rule involves flagging unnecessary non-null assertions.
The basic logic for this check is "if the type of the thing does not include null | undefined
then the non-null assertion is unnecessary".
By "the thing" I am referring to the expression being non-null asserted, eg the x
in x!
.
In the case of an "evolving any" the type of the variable is shown to be incorrect by the TS APIs.
As shown in the example code (specifically the y2
case) - the type of x
is presented as number
- even though it should be presented as number | undefined
. It appears that for some reason the non-null assertion modifies the type of the variable to remove the | undefined
, rather than just emitting a non-nullish type from the non-null assertion expression.
This is made even weirder from the fact that writing x as number
behaves correctly and the type of x
is correctly presented as number | undefined
.
This discrepancy is problematic for our lint rule because it means we currently false-positive on this case because when we query for the type of x
it looks non-nullish -- which suggests the non-null assertion is unnecessary. But if the user removes the non-null assertion then the type of x
changes to number | undefined
and this causes errors.