Skip to content

Commit 51b346d

Browse files
authored
Improve intersection reduction and CFA for truthy, equality, and typeof checks (#49119)
* Improve reduction of intersection types * Accept new baselines * Improve CFA for truthy, equality, and typeof checks * Accept new baselines * Remove special case for Function type * Don't reduce intersections of form {...} & object * Accept new baselines * Anything is assignable to unknown-like union * Accept new baselines * Tweak subtype check * Recombine unknown type from unknown-like union in more cases * Display union origin only if it is shorter than union itself * Accept new baselines * Add tests * Only attach origin type when it is shorter than union itself * Specially preserve string & {}, number & {}, bigint & {} * Accept new baselines * Add additional tests * Fix getNormalizedType and getNarrowableTypeForReference for intersections * Switch NonNullable<T> to use T & {} * Accept new baselines * Use NonNullable<T> in place of anonymous T & {} * Accept new baselines * Add fourslash test * More fourslash tests * Fix getFalsyFlags handling of intersections * Accept new baselines * Add constraint to compareProperties type parameter * Unconstrained type parameter not assignable to {} with strictNullChecks * Accept new baselines
1 parent ba45252 commit 51b346d

File tree

53 files changed

+3501
-305
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+3501
-305
lines changed

src/compiler/checker.ts

Lines changed: 137 additions & 78 deletions
Large diffs are not rendered by default.

src/compiler/core.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2035,7 +2035,7 @@ namespace ts {
20352035
return comparer(a, b);
20362036
}
20372037

2038-
export function compareProperties<T, K extends keyof T>(a: T | undefined, b: T | undefined, key: K, comparer: Comparer<T[K]>): Comparison {
2038+
export function compareProperties<T extends object, K extends keyof T>(a: T | undefined, b: T | undefined, key: K, comparer: Comparer<T[K]>): Comparison {
20392039
return a === b ? Comparison.EqualTo :
20402040
a === undefined ? Comparison.LessThan :
20412041
b === undefined ? Comparison.GreaterThan :

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,6 +1526,10 @@
15261526
"category": "Error",
15271527
"code": 2207
15281528
},
1529+
"This type parameter probably needs an `extends object` constraint.": {
1530+
"category": "Error",
1531+
"code": 2208
1532+
},
15291533

15301534
"The project root is ambiguous, but is required to resolve export map entry '{0}' in file '{1}'. Supply the `rootDir` compiler option to disambiguate.": {
15311535
"category": "Error",

src/compiler/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5397,6 +5397,11 @@ namespace ts {
53975397
// Flags that require TypeFlags.Union
53985398
/* @internal */
53995399
ContainsIntersections = 1 << 24, // Union contains intersections
5400+
/* @internal */
5401+
IsUnknownLikeUnionComputed = 1 << 25, // IsUnknownLikeUnion flag has been computed
5402+
/* @internal */
5403+
IsUnknownLikeUnion = 1 << 26, // Union of null, undefined, and empty object type
5404+
/* @internal */
54005405

54015406
// Flags that require TypeFlags.Intersection
54025407
/* @internal */

src/lib/es5.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1564,7 +1564,7 @@ type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
15641564
/**
15651565
* Exclude null and undefined from T
15661566
*/
1567-
type NonNullable<T> = T extends null | undefined ? never : T;
1567+
type NonNullable<T> = T & {};
15681568

15691569
/**
15701570
* Obtain the parameters of a function type in a tuple

tests/baselines/reference/assertionTypePredicates1.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ function f01(x: unknown) {
150150
>undefined : undefined
151151
>typeof x === "string" : boolean
152152
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
153-
>x : unknown
153+
>x : {} | null
154154
>"string" : "string"
155155

156156
x; // string | undefined

tests/baselines/reference/conditionalTypes1.errors.txt

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
11
tests/cases/conformance/types/conditional/conditionalTypes1.ts(12,5): error TS2322: Type 'T' is not assignable to type 'NonNullable<T>'.
2+
Type 'T' is not assignable to type '{}'.
23
tests/cases/conformance/types/conditional/conditionalTypes1.ts(17,5): error TS2322: Type 'T' is not assignable to type 'NonNullable<T>'.
34
Type 'string | undefined' is not assignable to type 'NonNullable<T>'.
45
Type 'undefined' is not assignable to type 'NonNullable<T>'.
6+
Type 'undefined' is not assignable to type 'T'.
7+
'undefined' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | undefined'.
8+
Type 'T' is not assignable to type '{}'.
9+
Type 'string | undefined' is not assignable to type '{}'.
10+
Type 'undefined' is not assignable to type '{}'.
511
tests/cases/conformance/types/conditional/conditionalTypes1.ts(24,5): error TS2322: Type 'T[keyof T] | undefined' is not assignable to type 'NonNullable<Partial<T>[keyof T]>'.
6-
Type 'undefined' is not assignable to type 'NonNullable<Partial<T>[keyof T]>'.
12+
Type 'undefined' is not assignable to type 'T[keyof T] & {}'.
13+
Type 'undefined' is not assignable to type 'T[keyof T]'.
714
tests/cases/conformance/types/conditional/conditionalTypes1.ts(29,5): error TS2322: Type 'T["x"]' is not assignable to type 'NonNullable<T["x"]>'.
815
Type 'string | undefined' is not assignable to type 'NonNullable<T["x"]>'.
916
Type 'undefined' is not assignable to type 'NonNullable<T["x"]>'.
17+
Type 'undefined' is not assignable to type '{}'.
18+
Type 'T["x"]' is not assignable to type '{}'.
19+
Type 'string | undefined' is not assignable to type '{}'.
20+
Type 'undefined' is not assignable to type '{}'.
1021
tests/cases/conformance/types/conditional/conditionalTypes1.ts(103,5): error TS2322: Type 'FunctionProperties<T>' is not assignable to type 'T'.
1122
'T' could be instantiated with an arbitrary type which could be unrelated to 'FunctionProperties<T>'.
1223
tests/cases/conformance/types/conditional/conditionalTypes1.ts(104,5): error TS2322: Type 'NonFunctionProperties<T>' is not assignable to type 'T'.
@@ -72,6 +83,8 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS
7283
y = x; // Error
7384
~
7485
!!! error TS2322: Type 'T' is not assignable to type 'NonNullable<T>'.
86+
!!! error TS2322: Type 'T' is not assignable to type '{}'.
87+
!!! related TS2208 tests/cases/conformance/types/conditional/conditionalTypes1.ts:10:13: This type parameter probably needs an `extends object` constraint.
7588
}
7689

7790
function f2<T extends string | undefined>(x: T, y: NonNullable<T>) {
@@ -81,6 +94,11 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS
8194
!!! error TS2322: Type 'T' is not assignable to type 'NonNullable<T>'.
8295
!!! error TS2322: Type 'string | undefined' is not assignable to type 'NonNullable<T>'.
8396
!!! error TS2322: Type 'undefined' is not assignable to type 'NonNullable<T>'.
97+
!!! error TS2322: Type 'undefined' is not assignable to type 'T'.
98+
!!! error TS2322: 'undefined' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | undefined'.
99+
!!! error TS2322: Type 'T' is not assignable to type '{}'.
100+
!!! error TS2322: Type 'string | undefined' is not assignable to type '{}'.
101+
!!! error TS2322: Type 'undefined' is not assignable to type '{}'.
84102
let s1: string = x; // Error
85103
let s2: string = y;
86104
}
@@ -90,7 +108,8 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS
90108
y = x; // Error
91109
~
92110
!!! error TS2322: Type 'T[keyof T] | undefined' is not assignable to type 'NonNullable<Partial<T>[keyof T]>'.
93-
!!! error TS2322: Type 'undefined' is not assignable to type 'NonNullable<Partial<T>[keyof T]>'.
111+
!!! error TS2322: Type 'undefined' is not assignable to type 'T[keyof T] & {}'.
112+
!!! error TS2322: Type 'undefined' is not assignable to type 'T[keyof T]'.
94113
}
95114

96115
function f4<T extends { x: string | undefined }>(x: T["x"], y: NonNullable<T["x"]>) {
@@ -100,6 +119,10 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS
100119
!!! error TS2322: Type 'T["x"]' is not assignable to type 'NonNullable<T["x"]>'.
101120
!!! error TS2322: Type 'string | undefined' is not assignable to type 'NonNullable<T["x"]>'.
102121
!!! error TS2322: Type 'undefined' is not assignable to type 'NonNullable<T["x"]>'.
122+
!!! error TS2322: Type 'undefined' is not assignable to type '{}'.
123+
!!! error TS2322: Type 'T["x"]' is not assignable to type '{}'.
124+
!!! error TS2322: Type 'string | undefined' is not assignable to type '{}'.
125+
!!! error TS2322: Type 'undefined' is not assignable to type '{}'.
103126
let s1: string = x; // Error
104127
let s2: string = y;
105128
}

tests/baselines/reference/conditionalTypes1.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function f2<T extends string | undefined>(x: T, y: NonNullable<T>) {
5555

5656
let s2: string = y;
5757
>s2 : string
58-
>y : NonNullable<T>
58+
>y : string
5959
}
6060

6161
function f3<T>(x: Partial<T>[keyof T], y: NonNullable<Partial<T>[keyof T]>) {
@@ -96,7 +96,7 @@ function f4<T extends { x: string | undefined }>(x: T["x"], y: NonNullable<T["x"
9696

9797
let s2: string = y;
9898
>s2 : string
99-
>y : NonNullable<T["x"]>
99+
>y : string
100100
}
101101

102102
type Options = { k: "a", a: number } | { k: "b", b: string } | { k: "c", c: boolean };

tests/baselines/reference/conditionalTypes2.errors.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ tests/cases/conformance/types/conditional/conditionalTypes2.ts(74,12): error TS2
3232
Property 'bat' is missing in type 'Foo & Bar' but required in type '{ foo: string; bat: string; }'.
3333
tests/cases/conformance/types/conditional/conditionalTypes2.ts(75,12): error TS2345: Argument of type 'Extract2<T, Foo, Bar>' is not assignable to parameter of type '{ foo: string; bat: string; }'.
3434
Type 'T extends Bar ? T : never' is not assignable to type '{ foo: string; bat: string; }'.
35-
Property 'bat' is missing in type 'Bar & Foo' but required in type '{ foo: string; bat: string; }'.
35+
Type 'Bar & Foo & T' is not assignable to type '{ foo: string; bat: string; }'.
3636

3737

3838
==== tests/cases/conformance/types/conditional/conditionalTypes2.ts (7 errors) ====
@@ -155,8 +155,7 @@ tests/cases/conformance/types/conditional/conditionalTypes2.ts(75,12): error TS2
155155
~
156156
!!! error TS2345: Argument of type 'Extract2<T, Foo, Bar>' is not assignable to parameter of type '{ foo: string; bat: string; }'.
157157
!!! error TS2345: Type 'T extends Bar ? T : never' is not assignable to type '{ foo: string; bat: string; }'.
158-
!!! error TS2345: Property 'bat' is missing in type 'Bar & Foo' but required in type '{ foo: string; bat: string; }'.
159-
!!! related TS2728 tests/cases/conformance/types/conditional/conditionalTypes2.ts:62:43: 'bat' is declared here.
158+
!!! error TS2345: Type 'Bar & Foo & T' is not assignable to type '{ foo: string; bat: string; }'.
160159
}
161160

162161
// Repros from #22860

tests/baselines/reference/controlFlowGenericTypes.types

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ function f1<T extends string | undefined>(x: T, y: { a: T }, z: [T]): string {
1010
>x : T
1111

1212
x;
13-
>x : T
13+
>x : NonNullable<T>
1414

1515
x.length;
1616
>x.length : number
@@ -67,7 +67,7 @@ function f2<T>(x: Extract<T, string | undefined> | null): string {
6767
>x : Extract<T, string | undefined> | null
6868

6969
x;
70-
>x : Extract<T, string | undefined>
70+
>x : NonNullable<Extract<T, string | undefined>>
7171

7272
x.length;
7373
>x.length : number
@@ -404,11 +404,11 @@ function fx1<T, K extends keyof T>(obj: T, key: K) {
404404
>key : K
405405

406406
const x2 = obj && obj[key];
407-
>x2 : T[K]
408-
>obj && obj[key] : T[K]
409-
>obj : T
410-
>obj[key] : T[K]
407+
>x2 : NonNullable<T>[K]
408+
>obj && obj[key] : NonNullable<T>[K]
411409
>obj : T
410+
>obj[key] : NonNullable<T>[K]
411+
>obj : NonNullable<T>
412412
>key : K
413413
}
414414

@@ -438,8 +438,8 @@ function fx3<T extends Record<keyof T, string> | undefined, K extends keyof T>(o
438438
>key : K
439439

440440
const x1 = obj[key]; // Error
441-
>x1 : NonNullable<Record<keyof T, string>>[K]
442-
>obj[key] : NonNullable<Record<keyof T, string>>[K]
441+
>x1 : Record<keyof T, string>[K]
442+
>obj[key] : Record<keyof T, string>[K]
443443
>obj : Record<keyof T, string> | undefined
444444
>key : K
445445

@@ -469,14 +469,14 @@ class TableBaseEnum<
469469
>null : null
470470

471471
iSpec[null! as keyof InternalSpec]; // Error, object possibly undefined
472-
>iSpec[null! as keyof InternalSpec] : NonNullable<Record<keyof PublicSpec, any>>[keyof InternalSpec]
472+
>iSpec[null! as keyof InternalSpec] : Record<keyof PublicSpec, any>[keyof InternalSpec]
473473
>iSpec : Record<keyof PublicSpec, any> | undefined
474474
>null! as keyof InternalSpec : keyof InternalSpec
475475
>null! : never
476476
>null : null
477477

478478
iSpec[null! as keyof PublicSpec]; // Error, object possibly undefined
479-
>iSpec[null! as keyof PublicSpec] : NonNullable<Record<keyof PublicSpec, any>>[keyof PublicSpec]
479+
>iSpec[null! as keyof PublicSpec] : Record<keyof PublicSpec, any>[keyof PublicSpec]
480480
>iSpec : Record<keyof PublicSpec, any> | undefined
481481
>null! as keyof PublicSpec : keyof PublicSpec
482482
>null! : never

tests/baselines/reference/controlFlowTruthiness.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ function f8<T>(x: T) {
181181
>x : T
182182

183183
x; // {}
184-
>x : T
184+
>x : NonNullable<T>
185185
}
186186
else {
187187
x; // {}

tests/baselines/reference/controlFlowTypeofObject.types

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ function f1(x: unknown) {
1616
if (typeof x === 'object') {
1717
>typeof x === 'object' : boolean
1818
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
19-
>x : unknown
19+
>x : {}
2020
>'object' : "object"
2121

2222
obj(x);
@@ -40,7 +40,7 @@ function f2(x: unknown) {
4040
if (typeof x === 'object') {
4141
>typeof x === 'object' : boolean
4242
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
43-
>x : unknown
43+
>x : {} | undefined
4444
>'object' : "object"
4545

4646
obj(x);
@@ -64,7 +64,7 @@ function f3(x: unknown) {
6464
if (typeof x === 'object') {
6565
>typeof x === 'object' : boolean
6666
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
67-
>x : unknown
67+
>x : {}
6868
>'object' : "object"
6969

7070
obj(x);
@@ -88,7 +88,7 @@ function f4(x: unknown) {
8888
if (typeof x === 'object') {
8989
>typeof x === 'object' : boolean
9090
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
91-
>x : unknown
91+
>x : {}
9292
>'object' : "object"
9393

9494
obj(x);
@@ -126,7 +126,7 @@ function f5(x: unknown) {
126126
if (typeof x === 'object') {
127127
>typeof x === 'object' : boolean
128128
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
129-
>x : unknown
129+
>x : {} | undefined
130130
>'object' : "object"
131131

132132
obj(x);
@@ -150,12 +150,12 @@ function f6(x: unknown) {
150150
}
151151
else {
152152
x;
153-
>x : unknown
153+
>x : {} | undefined
154154

155155
if (typeof x === 'object') {
156156
>typeof x === 'object' : boolean
157157
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
158-
>x : unknown
158+
>x : {} | undefined
159159
>'object' : "object"
160160

161161
obj(x);

0 commit comments

Comments
 (0)