{} & null and {} & undefined should always be never (#50553)

* {} & null and {} & undefined should be never in non-strictNullChecks mode

* Add tests

* Address code review feedback

* Accept new baselines
This commit is contained in:
Anders Hejlsberg 2022-09-01 12:37:13 -07:00 committed by GitHub
parent 238c341701
commit 6db2c882f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 347 additions and 1 deletions

View File

@ -15166,7 +15166,7 @@ namespace ts {
return includes & TypeFlags.IncludesWildcard ? wildcardType : anyType;
}
if (!strictNullChecks && includes & TypeFlags.Nullable) {
return includes & TypeFlags.Undefined ? undefinedType : nullType;
return includes & TypeFlags.IncludesEmptyObject ? neverType : includes & TypeFlags.Undefined ? undefinedType : nullType;
}
if (includes & TypeFlags.String && includes & (TypeFlags.StringLiteral | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) ||
includes & TypeFlags.Number && includes & TypeFlags.NumberLiteral ||

View File

@ -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

View File

@ -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))

View File

@ -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

View File

@ -0,0 +1,21 @@
//// [nonNullableAndObjectIntersections.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 TestNew = NonNullableNew<null>;
type TestOld = NonNullableOld<null>;
//// [nonNullableAndObjectIntersections.js]
// These should all resolve to never

View File

@ -0,0 +1,45 @@
=== tests/cases/compiler/nonNullableAndObjectIntersections.ts ===
// These should all resolve to never
type T0 = NonNullable<null>;
>T0 : Symbol(T0, Decl(nonNullableAndObjectIntersections.ts, 0, 0))
>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --))
type T1 = NonNullable<undefined>;
>T1 : Symbol(T1, Decl(nonNullableAndObjectIntersections.ts, 2, 28))
>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --))
type T2 = null & {};
>T2 : Symbol(T2, Decl(nonNullableAndObjectIntersections.ts, 3, 33))
type T3 = undefined & {};
>T3 : Symbol(T3, Decl(nonNullableAndObjectIntersections.ts, 4, 20))
type T4 = null & undefined;
>T4 : Symbol(T4, Decl(nonNullableAndObjectIntersections.ts, 5, 25))
type T6 = null & { a: string } & {};
>T6 : Symbol(T6, Decl(nonNullableAndObjectIntersections.ts, 6, 27))
>a : Symbol(a, Decl(nonNullableAndObjectIntersections.ts, 7, 18))
// Repro from #50519
type NonNullableNew<T> = T & {};
>NonNullableNew : Symbol(NonNullableNew, Decl(nonNullableAndObjectIntersections.ts, 7, 36))
>T : Symbol(T, Decl(nonNullableAndObjectIntersections.ts, 11, 20))
>T : Symbol(T, Decl(nonNullableAndObjectIntersections.ts, 11, 20))
type NonNullableOld<T> = T extends null | undefined ? never : T;
>NonNullableOld : Symbol(NonNullableOld, Decl(nonNullableAndObjectIntersections.ts, 11, 32))
>T : Symbol(T, Decl(nonNullableAndObjectIntersections.ts, 12, 20))
>T : Symbol(T, Decl(nonNullableAndObjectIntersections.ts, 12, 20))
>T : Symbol(T, Decl(nonNullableAndObjectIntersections.ts, 12, 20))
type TestNew = NonNullableNew<null>;
>TestNew : Symbol(TestNew, Decl(nonNullableAndObjectIntersections.ts, 12, 64))
>NonNullableNew : Symbol(NonNullableNew, Decl(nonNullableAndObjectIntersections.ts, 7, 36))
type TestOld = NonNullableOld<null>;
>TestOld : Symbol(TestOld, Decl(nonNullableAndObjectIntersections.ts, 14, 36))
>NonNullableOld : Symbol(NonNullableOld, Decl(nonNullableAndObjectIntersections.ts, 11, 32))

View File

@ -0,0 +1,43 @@
=== tests/cases/compiler/nonNullableAndObjectIntersections.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 TestNew = NonNullableNew<null>;
>TestNew : never
>null : null
type TestOld = NonNullableOld<null>;
>TestOld : never
>null : null

View File

@ -0,0 +1,22 @@
//// [nonNullableAndObjectIntersections.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 TestNew = NonNullableNew<null>;
type TestOld = NonNullableOld<null>;
//// [nonNullableAndObjectIntersections.js]
"use strict";
// These should all resolve to never

View File

@ -0,0 +1,45 @@
=== tests/cases/compiler/nonNullableAndObjectIntersections.ts ===
// These should all resolve to never
type T0 = NonNullable<null>;
>T0 : Symbol(T0, Decl(nonNullableAndObjectIntersections.ts, 0, 0))
>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --))
type T1 = NonNullable<undefined>;
>T1 : Symbol(T1, Decl(nonNullableAndObjectIntersections.ts, 2, 28))
>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --))
type T2 = null & {};
>T2 : Symbol(T2, Decl(nonNullableAndObjectIntersections.ts, 3, 33))
type T3 = undefined & {};
>T3 : Symbol(T3, Decl(nonNullableAndObjectIntersections.ts, 4, 20))
type T4 = null & undefined;
>T4 : Symbol(T4, Decl(nonNullableAndObjectIntersections.ts, 5, 25))
type T6 = null & { a: string } & {};
>T6 : Symbol(T6, Decl(nonNullableAndObjectIntersections.ts, 6, 27))
>a : Symbol(a, Decl(nonNullableAndObjectIntersections.ts, 7, 18))
// Repro from #50519
type NonNullableNew<T> = T & {};
>NonNullableNew : Symbol(NonNullableNew, Decl(nonNullableAndObjectIntersections.ts, 7, 36))
>T : Symbol(T, Decl(nonNullableAndObjectIntersections.ts, 11, 20))
>T : Symbol(T, Decl(nonNullableAndObjectIntersections.ts, 11, 20))
type NonNullableOld<T> = T extends null | undefined ? never : T;
>NonNullableOld : Symbol(NonNullableOld, Decl(nonNullableAndObjectIntersections.ts, 11, 32))
>T : Symbol(T, Decl(nonNullableAndObjectIntersections.ts, 12, 20))
>T : Symbol(T, Decl(nonNullableAndObjectIntersections.ts, 12, 20))
>T : Symbol(T, Decl(nonNullableAndObjectIntersections.ts, 12, 20))
type TestNew = NonNullableNew<null>;
>TestNew : Symbol(TestNew, Decl(nonNullableAndObjectIntersections.ts, 12, 64))
>NonNullableNew : Symbol(NonNullableNew, Decl(nonNullableAndObjectIntersections.ts, 7, 36))
type TestOld = NonNullableOld<null>;
>TestOld : Symbol(TestOld, Decl(nonNullableAndObjectIntersections.ts, 14, 36))
>NonNullableOld : Symbol(NonNullableOld, Decl(nonNullableAndObjectIntersections.ts, 11, 32))

View File

@ -0,0 +1,43 @@
=== tests/cases/compiler/nonNullableAndObjectIntersections.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 TestNew = NonNullableNew<null>;
>TestNew : never
>null : null
type TestOld = NonNullableOld<null>;
>TestOld : never
>null : null

View File

@ -0,0 +1,18 @@
// @strict: true, false
// 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 TestNew = NonNullableNew<null>;
type TestOld = NonNullableOld<null>;