Skip to content
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

Improve intersection reduction and CFA for truthy, equality, and typeof checks #49119

Merged
merged 32 commits into from
May 27, 2022
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ce23b8d
Improve reduction of intersection types
ahejlsberg May 5, 2022
ffe147e
Accept new baselines
ahejlsberg May 5, 2022
a95de48
Merge branch 'main' into improveIntersectionReduction
ahejlsberg May 13, 2022
29edef1
Improve CFA for truthy, equality, and typeof checks
ahejlsberg May 15, 2022
39326d7
Accept new baselines
ahejlsberg May 15, 2022
d0b7ec8
Remove special case for Function type
ahejlsberg May 15, 2022
a25388d
Merge branch 'main' into improveIntersectionReduction
ahejlsberg May 15, 2022
95c3c56
Don't reduce intersections of form {...} & object
ahejlsberg May 15, 2022
986963c
Accept new baselines
ahejlsberg May 15, 2022
c400181
Anything is assignable to unknown-like union
ahejlsberg May 16, 2022
f1ebcdd
Accept new baselines
ahejlsberg May 16, 2022
73c91fd
Tweak subtype check
ahejlsberg May 16, 2022
722c462
Recombine unknown type from unknown-like union in more cases
ahejlsberg May 17, 2022
5e1111a
Display union origin only if it is shorter than union itself
ahejlsberg May 18, 2022
5c579a4
Accept new baselines
ahejlsberg May 18, 2022
8913cf0
Add tests
ahejlsberg May 18, 2022
dbce210
Only attach origin type when it is shorter than union itself
ahejlsberg May 19, 2022
1913c94
Specially preserve string & {}, number & {}, bigint & {}
ahejlsberg May 20, 2022
8c7a38b
Accept new baselines
ahejlsberg May 20, 2022
6f18e90
Add additional tests
ahejlsberg May 20, 2022
e70bf7c
Fix getNormalizedType and getNarrowableTypeForReference for intersect…
ahejlsberg May 20, 2022
7a62983
Switch NonNullable<T> to use T & {}
ahejlsberg May 20, 2022
c35cba7
Accept new baselines
ahejlsberg May 20, 2022
a0c0929
Use NonNullable<T> in place of anonymous T & {}
ahejlsberg May 20, 2022
eff3be3
Accept new baselines
ahejlsberg May 20, 2022
d3eb84f
Add fourslash test
ahejlsberg May 24, 2022
954e80a
More fourslash tests
ahejlsberg May 24, 2022
1d9b53f
Fix getFalsyFlags handling of intersections
ahejlsberg May 26, 2022
3815934
Accept new baselines
ahejlsberg May 26, 2022
bfa3a90
Add constraint to compareProperties type parameter
ahejlsberg May 26, 2022
9801667
Unconstrained type parameter not assignable to {} with strictNullChecks
ahejlsberg May 26, 2022
51da289
Accept new baselines
ahejlsberg May 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 134 additions & 71 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2035,7 +2035,7 @@ namespace ts {
return comparer(a, b);
}

export function compareProperties<T, K extends keyof T>(a: T | undefined, b: T | undefined, key: K, comparer: Comparer<T[K]>): Comparison {
export function compareProperties<T extends object, K extends keyof T>(a: T | undefined, b: T | undefined, key: K, comparer: Comparer<T[K]>): Comparison {
return a === b ? Comparison.EqualTo :
a === undefined ? Comparison.LessThan :
b === undefined ? Comparison.GreaterThan :
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5397,6 +5397,11 @@ namespace ts {
// Flags that require TypeFlags.Union
/* @internal */
ContainsIntersections = 1 << 24, // Union contains intersections
/* @internal */
IsUnknownLikeUnionComputed = 1 << 25, // IsUnknownLikeUnion flag has been computed
/* @internal */
IsUnknownLikeUnion = 1 << 26, // Union of null, undefined, and empty object type
/* @internal */

// Flags that require TypeFlags.Intersection
/* @internal */
Expand Down
2 changes: 1 addition & 1 deletion src/lib/es5.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1564,7 +1564,7 @@ type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
/**
* Exclude null and undefined from T
*/
type NonNullable<T> = T extends null | undefined ? never : T;
type NonNullable<T> = T & {};

/**
* Obtain the parameters of a function type in a tuple
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/assertionTypePredicates1.types
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ function f01(x: unknown) {
>undefined : undefined
>typeof x === "string" : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : unknown
>x : {} | null
>"string" : "string"

x; // string | undefined
Expand Down
29 changes: 23 additions & 6 deletions tests/baselines/reference/conditionalTypes1.errors.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
tests/cases/conformance/types/conditional/conditionalTypes1.ts(12,5): error TS2322: Type 'T' is not assignable to type 'NonNullable<T>'.
tests/cases/conformance/types/conditional/conditionalTypes1.ts(17,5): error TS2322: Type 'T' is not assignable to type 'NonNullable<T>'.
Type 'string | undefined' is not assignable to type 'NonNullable<T>'.
Type 'undefined' is not assignable to type 'NonNullable<T>'.
Type 'undefined' is not assignable to type 'T'.
'undefined' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | undefined'.
Type 'T' is not assignable to type '{}'.
Type 'string | undefined' is not assignable to type '{}'.
Type 'undefined' is not assignable to type '{}'.
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]>'.
Type 'undefined' is not assignable to type 'NonNullable<Partial<T>[keyof T]>'.
Type 'undefined' is not assignable to type 'T[keyof T] & {}'.
Type 'undefined' is not assignable to type 'T[keyof T]'.
tests/cases/conformance/types/conditional/conditionalTypes1.ts(29,5): error TS2322: Type 'T["x"]' is not assignable to type 'NonNullable<T["x"]>'.
Type 'string | undefined' is not assignable to type 'NonNullable<T["x"]>'.
Type 'undefined' is not assignable to type 'NonNullable<T["x"]>'.
Type 'undefined' is not assignable to type '{}'.
Type 'T["x"]' is not assignable to type '{}'.
Type 'string | undefined' is not assignable to type '{}'.
Type 'undefined' is not assignable to type '{}'.
tests/cases/conformance/types/conditional/conditionalTypes1.ts(103,5): error TS2322: Type 'FunctionProperties<T>' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'FunctionProperties<T>'.
tests/cases/conformance/types/conditional/conditionalTypes1.ts(104,5): error TS2322: Type 'NonFunctionProperties<T>' is not assignable to type 'T'.
Expand Down Expand Up @@ -57,7 +66,7 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS
Type 'boolean' is not assignable to type 'true'.


==== tests/cases/conformance/types/conditional/conditionalTypes1.ts (20 errors) ====
==== tests/cases/conformance/types/conditional/conditionalTypes1.ts (19 errors) ====
type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"

Expand All @@ -70,8 +79,6 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS
function f1<T>(x: T, y: NonNullable<T>) {
x = y;
y = x; // Error
~
!!! error TS2322: Type 'T' is not assignable to type 'NonNullable<T>'.
}

function f2<T extends string | undefined>(x: T, y: NonNullable<T>) {
Expand All @@ -81,6 +88,11 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS
!!! error TS2322: Type 'T' is not assignable to type 'NonNullable<T>'.
!!! error TS2322: Type 'string | undefined' is not assignable to type 'NonNullable<T>'.
!!! error TS2322: Type 'undefined' is not assignable to type 'NonNullable<T>'.
!!! error TS2322: Type 'undefined' is not assignable to type 'T'.
!!! error TS2322: 'undefined' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | undefined'.
!!! error TS2322: Type 'T' is not assignable to type '{}'.
!!! error TS2322: Type 'string | undefined' is not assignable to type '{}'.
!!! error TS2322: Type 'undefined' is not assignable to type '{}'.
let s1: string = x; // Error
let s2: string = y;
}
Expand All @@ -90,7 +102,8 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS
y = x; // Error
~
!!! error TS2322: Type 'T[keyof T] | undefined' is not assignable to type 'NonNullable<Partial<T>[keyof T]>'.
!!! error TS2322: Type 'undefined' is not assignable to type 'NonNullable<Partial<T>[keyof T]>'.
!!! error TS2322: Type 'undefined' is not assignable to type 'T[keyof T] & {}'.
!!! error TS2322: Type 'undefined' is not assignable to type 'T[keyof T]'.
}

function f4<T extends { x: string | undefined }>(x: T["x"], y: NonNullable<T["x"]>) {
Expand All @@ -100,6 +113,10 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS
!!! error TS2322: Type 'T["x"]' is not assignable to type 'NonNullable<T["x"]>'.
!!! error TS2322: Type 'string | undefined' is not assignable to type 'NonNullable<T["x"]>'.
!!! error TS2322: Type 'undefined' is not assignable to type 'NonNullable<T["x"]>'.
!!! error TS2322: Type 'undefined' is not assignable to type '{}'.
!!! error TS2322: Type 'T["x"]' is not assignable to type '{}'.
!!! error TS2322: Type 'string | undefined' is not assignable to type '{}'.
!!! error TS2322: Type 'undefined' is not assignable to type '{}'.
let s1: string = x; // Error
let s2: string = y;
}
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/conditionalTypes1.types
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function f2<T extends string | undefined>(x: T, y: NonNullable<T>) {

let s2: string = y;
>s2 : string
>y : NonNullable<T>
>y : string
}

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

let s2: string = y;
>s2 : string
>y : NonNullable<T["x"]>
>y : string
}

type Options = { k: "a", a: number } | { k: "b", b: string } | { k: "c", c: boolean };
Expand Down
5 changes: 2 additions & 3 deletions tests/baselines/reference/conditionalTypes2.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ tests/cases/conformance/types/conditional/conditionalTypes2.ts(74,12): error TS2
Property 'bat' is missing in type 'Foo & Bar' but required in type '{ foo: string; bat: string; }'.
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; }'.
Type 'T extends Bar ? T : never' is not assignable to type '{ foo: string; bat: string; }'.
Property 'bat' is missing in type 'Bar & Foo' but required in type '{ foo: string; bat: string; }'.
Type 'Bar & Foo & T' is not assignable to type '{ foo: string; bat: string; }'.


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

// Repros from #22860
Expand Down
20 changes: 10 additions & 10 deletions tests/baselines/reference/controlFlowGenericTypes.types
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function f1<T extends string | undefined>(x: T, y: { a: T }, z: [T]): string {
>x : T

x;
>x : T
>x : NonNullable<T>

x.length;
>x.length : number
Expand Down Expand Up @@ -67,7 +67,7 @@ function f2<T>(x: Extract<T, string | undefined> | null): string {
>x : Extract<T, string | undefined> | null

x;
>x : Extract<T, string | undefined>
>x : NonNullable<Extract<T, string | undefined>>

x.length;
>x.length : number
Expand Down Expand Up @@ -404,11 +404,11 @@ function fx1<T, K extends keyof T>(obj: T, key: K) {
>key : K

const x2 = obj && obj[key];
>x2 : T[K]
>obj && obj[key] : T[K]
>obj : T
>obj[key] : T[K]
>x2 : NonNullable<T>[K]
>obj && obj[key] : NonNullable<T>[K]
>obj : T
>obj[key] : NonNullable<T>[K]
>obj : NonNullable<T>
>key : K
}

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

const x1 = obj[key]; // Error
>x1 : NonNullable<Record<keyof T, string>>[K]
>obj[key] : NonNullable<Record<keyof T, string>>[K]
>x1 : Record<keyof T, string>[K]
>obj[key] : Record<keyof T, string>[K]
>obj : Record<keyof T, string> | undefined
>key : K

Expand Down Expand Up @@ -469,14 +469,14 @@ class TableBaseEnum<
>null : null

iSpec[null! as keyof InternalSpec]; // Error, object possibly undefined
>iSpec[null! as keyof InternalSpec] : NonNullable<Record<keyof PublicSpec, any>>[keyof InternalSpec]
>iSpec[null! as keyof InternalSpec] : Record<keyof PublicSpec, any>[keyof InternalSpec]
>iSpec : Record<keyof PublicSpec, any> | undefined
>null! as keyof InternalSpec : keyof InternalSpec
>null! : never
>null : null

iSpec[null! as keyof PublicSpec]; // Error, object possibly undefined
>iSpec[null! as keyof PublicSpec] : NonNullable<Record<keyof PublicSpec, any>>[keyof PublicSpec]
>iSpec[null! as keyof PublicSpec] : Record<keyof PublicSpec, any>[keyof PublicSpec]
>iSpec : Record<keyof PublicSpec, any> | undefined
>null! as keyof PublicSpec : keyof PublicSpec
>null! : never
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/controlFlowTruthiness.types
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ function f8<T>(x: T) {
>x : T

x; // {}
>x : T
>x : NonNullable<T>
}
else {
x; // {}
Expand Down
14 changes: 7 additions & 7 deletions tests/baselines/reference/controlFlowTypeofObject.types
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function f1(x: unknown) {
if (typeof x === 'object') {
>typeof x === 'object' : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : unknown
>x : {}
>'object' : "object"

obj(x);
Expand All @@ -40,7 +40,7 @@ function f2(x: unknown) {
if (typeof x === 'object') {
>typeof x === 'object' : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : unknown
>x : {} | undefined
>'object' : "object"

obj(x);
Expand All @@ -64,7 +64,7 @@ function f3(x: unknown) {
if (typeof x === 'object') {
>typeof x === 'object' : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : unknown
>x : {}
>'object' : "object"

obj(x);
Expand All @@ -88,7 +88,7 @@ function f4(x: unknown) {
if (typeof x === 'object') {
>typeof x === 'object' : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : unknown
>x : {}
>'object' : "object"

obj(x);
Expand Down Expand Up @@ -126,7 +126,7 @@ function f5(x: unknown) {
if (typeof x === 'object') {
>typeof x === 'object' : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : unknown
>x : {} | undefined
>'object' : "object"

obj(x);
Expand All @@ -150,12 +150,12 @@ function f6(x: unknown) {
}
else {
x;
>x : unknown
>x : {} | undefined

if (typeof x === 'object') {
>typeof x === 'object' : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : unknown
>x : {} | undefined
>'object' : "object"

obj(x);
Expand Down
Loading