New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
{} & null and {} & undefined should always be never #50553
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
//// [NonNullableInNonStrictMode.ts] | ||
// These should all resolve to never | ||
|
||
type T0 = NonNullable<null>; | ||
type T1 = NonNullable<undefined>; | ||
type T2 = null & {}; | ||
type T3 = undefined & {}; | ||
type T4 = null & undefined; | ||
type T6 = null & { a: string } & {}; | ||
|
||
// Repro from #50519 | ||
|
||
type NonNullableNew<T> = T & {}; | ||
type NonNullableOld<T> = T extends null | undefined ? never : T; | ||
|
||
type IsNullWithoutStrictNullChecks = NonNullableNew<null>; | ||
type IsAlwaysNever = NonNullableOld<null>; | ||
|
||
|
||
//// [NonNullableInNonStrictMode.js] | ||
// These should all resolve to never |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
=== tests/cases/compiler/NonNullableInNonStrictMode.ts === | ||
// These should all resolve to never | ||
|
||
type T0 = NonNullable<null>; | ||
>T0 : Symbol(T0, Decl(NonNullableInNonStrictMode.ts, 0, 0)) | ||
>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --)) | ||
|
||
type T1 = NonNullable<undefined>; | ||
>T1 : Symbol(T1, Decl(NonNullableInNonStrictMode.ts, 2, 28)) | ||
>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --)) | ||
|
||
type T2 = null & {}; | ||
>T2 : Symbol(T2, Decl(NonNullableInNonStrictMode.ts, 3, 33)) | ||
|
||
type T3 = undefined & {}; | ||
>T3 : Symbol(T3, Decl(NonNullableInNonStrictMode.ts, 4, 20)) | ||
|
||
type T4 = null & undefined; | ||
>T4 : Symbol(T4, Decl(NonNullableInNonStrictMode.ts, 5, 25)) | ||
|
||
type T6 = null & { a: string } & {}; | ||
>T6 : Symbol(T6, Decl(NonNullableInNonStrictMode.ts, 6, 27)) | ||
>a : Symbol(a, Decl(NonNullableInNonStrictMode.ts, 7, 18)) | ||
|
||
// Repro from #50519 | ||
|
||
type NonNullableNew<T> = T & {}; | ||
>NonNullableNew : Symbol(NonNullableNew, Decl(NonNullableInNonStrictMode.ts, 7, 36)) | ||
>T : Symbol(T, Decl(NonNullableInNonStrictMode.ts, 11, 20)) | ||
>T : Symbol(T, Decl(NonNullableInNonStrictMode.ts, 11, 20)) | ||
|
||
type NonNullableOld<T> = T extends null | undefined ? never : T; | ||
>NonNullableOld : Symbol(NonNullableOld, Decl(NonNullableInNonStrictMode.ts, 11, 32)) | ||
>T : Symbol(T, Decl(NonNullableInNonStrictMode.ts, 12, 20)) | ||
>T : Symbol(T, Decl(NonNullableInNonStrictMode.ts, 12, 20)) | ||
>T : Symbol(T, Decl(NonNullableInNonStrictMode.ts, 12, 20)) | ||
|
||
type IsNullWithoutStrictNullChecks = NonNullableNew<null>; | ||
>IsNullWithoutStrictNullChecks : Symbol(IsNullWithoutStrictNullChecks, Decl(NonNullableInNonStrictMode.ts, 12, 64)) | ||
>NonNullableNew : Symbol(NonNullableNew, Decl(NonNullableInNonStrictMode.ts, 7, 36)) | ||
|
||
type IsAlwaysNever = NonNullableOld<null>; | ||
>IsAlwaysNever : Symbol(IsAlwaysNever, Decl(NonNullableInNonStrictMode.ts, 14, 58)) | ||
>NonNullableOld : Symbol(NonNullableOld, Decl(NonNullableInNonStrictMode.ts, 11, 32)) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
=== tests/cases/compiler/NonNullableInNonStrictMode.ts === | ||
// These should all resolve to never | ||
|
||
type T0 = NonNullable<null>; | ||
>T0 : never | ||
>null : null | ||
|
||
type T1 = NonNullable<undefined>; | ||
>T1 : never | ||
|
||
type T2 = null & {}; | ||
>T2 : never | ||
>null : null | ||
|
||
type T3 = undefined & {}; | ||
>T3 : never | ||
|
||
type T4 = null & undefined; | ||
>T4 : never | ||
>null : null | ||
|
||
type T6 = null & { a: string } & {}; | ||
>T6 : never | ||
>null : null | ||
>a : string | ||
|
||
// Repro from #50519 | ||
|
||
type NonNullableNew<T> = T & {}; | ||
>NonNullableNew : NonNullableNew<T> | ||
|
||
type NonNullableOld<T> = T extends null | undefined ? never : T; | ||
>NonNullableOld : NonNullableOld<T> | ||
>null : null | ||
|
||
type IsNullWithoutStrictNullChecks = NonNullableNew<null>; | ||
>IsNullWithoutStrictNullChecks : never | ||
>null : null | ||
|
||
type IsAlwaysNever = NonNullableOld<null>; | ||
>IsAlwaysNever : never | ||
>null : null | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,18 @@ | ||||||
// @strict: false | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be good to actually test these behaviors in both strict on and off.
Suggested change
Then rename the test to |
||||||
|
||||||
// These should all resolve to never | ||||||
|
||||||
type T0 = NonNullable<null>; | ||||||
type T1 = NonNullable<undefined>; | ||||||
type T2 = null & {}; | ||||||
type T3 = undefined & {}; | ||||||
type T4 = null & undefined; | ||||||
type T6 = null & { a: string } & {}; | ||||||
|
||||||
// Repro from #50519 | ||||||
|
||||||
type NonNullableNew<T> = T & {}; | ||||||
type NonNullableOld<T> = T extends null | undefined ? never : T; | ||||||
|
||||||
type IsNullWithoutStrictNullChecks = NonNullableNew<null>; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: this name seems to be confusing to me since it doesn't actually describe the expected result (if I understand correctly) but rather it refers to what was a bug |
||||||
type IsAlwaysNever = NonNullableOld<null>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't we try to delete this whole
!strictNullChecks
branch entirely? Or are you still fine withundefined & null
being weirdlyundefined
in non-strict butnever
in strict?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need that check for cases where the intersection is a singleton
null
orundefined
.undefined & null
is already resolved tonever
by an earlier check.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, yeah, duh, I know why we can just delete this branch - 15 lines up there's a
strictNullChecks
only branch that does this already for strict mode - we can just delete both thatstrictNullChecks
gate and this entire conditional.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nah, that branch also resolves
null & object
andnull & { foo: string }
tonever
, which we don't want to do in non-strictNullChecks mode.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But why not? If we're already adjusting
null & {}
, isn't that just a logical followup?null & {x}
seems even more suspect thannull & {}
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The whole premise of
null
andundefined
being in the domain of every type is suspect. But that's non-strictNullChecks. My only objective here is to better align with pre-4.8 behavior in a corner case.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would be open to seeing what happens and considering such a change for 4.9, but I think this workaround for 4.8 is okay for now.