From ef8d5094b41c62ab484753acd32d872508423284 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 24 Apr 2018 14:57:27 -0700 Subject: [PATCH 1/9] Properly check relationships for intersections with union constraints --- src/compiler/checker.ts | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 00ddb416e2263..8d4301c8722e1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6428,6 +6428,26 @@ namespace ts { return getConstraintOfDistributiveConditionalType(type) || getDefaultConstraintOfConditionalType(type); } + function getUnionConstraintOfIntersection(type: IntersectionType) { + let constraints: Type[]; + for (const t of type.types) { + if (t.flags & TypeFlags.Instantiable) { + const baseConstraint = getBaseConstraintOfType(t); + if (!(baseConstraint.flags & TypeFlags.Union)) { + return undefined; + } + constraints = append(constraints, baseConstraint); + } + } + if (constraints) { + const constraint = getIntersectionType(constraints); + if (constraint.flags & TypeFlags.Union) { + return constraint; + } + } + return undefined; + } + function getBaseConstraintOfInstantiableNonPrimitiveUnionOrIntersection(type: Type) { if (type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.UnionOrIntersection)) { const constraint = getResolvedBaseConstraint(type); @@ -10125,6 +10145,21 @@ namespace ts { errorInfo = saveErrorInfo; } } + if (!result && source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Union) { + // The combined constraint of an intersection type is the intersection of the constraints of + // the constituents. Since we break down union types before intersection types, if the target + // type is a union type and the source type is an intersection type with a combined constraint + // that is a union type, we need an extra check to see if the combined constraint is related to + // the target. For example, given two type variables T and U, each with the constraint + // 'string | number', the combined constraint of 'T & U' is 'string | number' and we need to + // check this constraint against the union on the target side. + const constraint = getUnionConstraintOfIntersection(source); + if (constraint) { + if (result = isRelatedTo(constraint, target, reportErrors)) { + errorInfo = saveErrorInfo; + } + } + } } isIntersectionConstituent = saveIsIntersectionConstituent; From d90d6b9277953812d741b5fd43f097ea59d5d247 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 24 Apr 2018 15:55:15 -0700 Subject: [PATCH 2/9] Remove more intersections with empty value domains from union types --- src/compiler/checker.ts | 15 ++++++++++++--- src/compiler/types.ts | 1 + 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8d4301c8722e1..0618abe75bb4f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7917,8 +7917,13 @@ namespace ts { return binarySearch(types, type, getTypeId, compareValues) >= 0; } - // Return true if the given intersection type contains (a) more than one unit type or (b) an object - // type and a nullable type (null or undefined). + // Return true if the given intersection type contains + // more than one unit type or, + // an object type and a nullable type (null or undefined), or + // a string-like type and a non-string-like primitive type, or + // a number-like type and a non-number-like primitive type, or + // a symbol-like type and a non-symbol-like primitive type, or + // a void-like type and a non-void-like primitive type. function isEmptyIntersectionType(type: IntersectionType) { let combined: TypeFlags = 0; for (const t of type.types) { @@ -7926,7 +7931,11 @@ namespace ts { return true; } combined |= t.flags; - if (combined & TypeFlags.Nullable && combined & (TypeFlags.Object | TypeFlags.NonPrimitive)) { + if (combined & TypeFlags.Nullable && combined & (TypeFlags.Object | TypeFlags.NonPrimitive) || + combined & TypeFlags.StringLike && combined & (TypeFlags.Primitive & ~TypeFlags.StringLike) || + combined & TypeFlags.NumberLike && combined & (TypeFlags.Primitive & ~TypeFlags.NumberLike) || + combined & TypeFlags.ESSymbolLike && combined & (TypeFlags.Primitive & ~TypeFlags.ESSymbolLike) || + combined & TypeFlags.VoidLike && combined & (TypeFlags.Primitive & ~TypeFlags.VoidLike)) { return true; } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 06f15e1d77a50..fd3a58bc5ea55 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3613,6 +3613,7 @@ namespace ts { BooleanLike = Boolean | BooleanLiteral, EnumLike = Enum | EnumLiteral, ESSymbolLike = ESSymbol | UniqueESSymbol, + VoidLike = Void | Undefined, UnionOrIntersection = Union | Intersection, StructuredType = Object | Union | Intersection, TypeVariable = TypeParameter | IndexedAccess, From e091e350bb2c58e6c922b61f3928f72e7b536b0e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 24 Apr 2018 15:56:46 -0700 Subject: [PATCH 3/9] Accept new baselines --- tests/baselines/reference/api/tsserverlibrary.d.ts | 1 + tests/baselines/reference/api/typescript.d.ts | 1 + .../errorMessagesIntersectionTypes04.errors.txt | 7 +------ .../reference/errorMessagesIntersectionTypes04.types | 6 +++--- .../reference/keyofAndIndexedAccessErrors.types | 8 ++++---- .../switchCaseWithIntersectionTypes01.errors.txt | 9 +-------- .../reference/switchCaseWithIntersectionTypes01.types | 4 ++-- tests/baselines/reference/typeGuardsWithInstanceOf.types | 4 ++-- 8 files changed, 15 insertions(+), 25 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 9bd2de6aa41d2..fb261ef4b4642 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2105,6 +2105,7 @@ declare namespace ts { BooleanLike = 136, EnumLike = 272, ESSymbolLike = 1536, + VoidLike = 6144, UnionOrIntersection = 393216, StructuredType = 458752, TypeVariable = 1081344, diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 3c47002061ffc..39f811bcec542 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2105,6 +2105,7 @@ declare namespace ts { BooleanLike = 136, EnumLike = 272, ESSymbolLike = 1536, + VoidLike = 6144, UnionOrIntersection = 393216, StructuredType = 458752, TypeVariable = 1081344, diff --git a/tests/baselines/reference/errorMessagesIntersectionTypes04.errors.txt b/tests/baselines/reference/errorMessagesIntersectionTypes04.errors.txt index d2e81844da151..25fdc65f5e8eb 100644 --- a/tests/baselines/reference/errorMessagesIntersectionTypes04.errors.txt +++ b/tests/baselines/reference/errorMessagesIntersectionTypes04.errors.txt @@ -1,11 +1,9 @@ tests/cases/compiler/errorMessagesIntersectionTypes04.ts(17,5): error TS2322: Type 'A & B' is not assignable to type 'number'. tests/cases/compiler/errorMessagesIntersectionTypes04.ts(18,5): error TS2322: Type 'A & B' is not assignable to type 'boolean'. tests/cases/compiler/errorMessagesIntersectionTypes04.ts(19,5): error TS2322: Type 'A & B' is not assignable to type 'string'. -tests/cases/compiler/errorMessagesIntersectionTypes04.ts(21,5): error TS2322: Type '(number & true) | (number & false)' is not assignable to type 'string'. - Type 'number & true' is not assignable to type 'string'. -==== tests/cases/compiler/errorMessagesIntersectionTypes04.ts (4 errors) ==== +==== tests/cases/compiler/errorMessagesIntersectionTypes04.ts (3 errors) ==== interface A { a; } @@ -33,7 +31,4 @@ tests/cases/compiler/errorMessagesIntersectionTypes04.ts(21,5): error TS2322: Ty !!! error TS2322: Type 'A & B' is not assignable to type 'string'. str = num_and_bool; - ~~~ -!!! error TS2322: Type '(number & true) | (number & false)' is not assignable to type 'string'. -!!! error TS2322: Type 'number & true' is not assignable to type 'string'. } \ No newline at end of file diff --git a/tests/baselines/reference/errorMessagesIntersectionTypes04.types b/tests/baselines/reference/errorMessagesIntersectionTypes04.types index 9d8db5dba3d46..b24597d4b3389 100644 --- a/tests/baselines/reference/errorMessagesIntersectionTypes04.types +++ b/tests/baselines/reference/errorMessagesIntersectionTypes04.types @@ -36,7 +36,7 @@ function f(): void { >B : B let num_and_bool: number & boolean; ->num_and_bool : (number & true) | (number & false) +>num_and_bool : never num = a_and_b; >num = a_and_b : A & B @@ -54,7 +54,7 @@ function f(): void { >a_and_b : A & B str = num_and_bool; ->str = num_and_bool : (number & true) | (number & false) +>str = num_and_bool : never >str : string ->num_and_bool : (number & true) | (number & false) +>num_and_bool : never } diff --git a/tests/baselines/reference/keyofAndIndexedAccessErrors.types b/tests/baselines/reference/keyofAndIndexedAccessErrors.types index 6a474c1810729..6f90eb2c18797 100644 --- a/tests/baselines/reference/keyofAndIndexedAccessErrors.types +++ b/tests/baselines/reference/keyofAndIndexedAccessErrors.types @@ -34,19 +34,19 @@ type T02 = keyof keyof Object; >Object : Object type T03 = keyof keyof keyof Object; ->T03 : "toString" | "valueOf" | ("toString" & number) | ("toLocaleString" & number) | ("valueOf" & number) | ("toFixed" & number) | ("toExponential" & number) | ("toPrecision" & number) +>T03 : "toString" | "valueOf" >Object : Object type T04 = keyof keyof keyof keyof Object; ->T04 : number | "length" | "toString" | "valueOf" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | ("toString" & number) | ("valueOf" & number) | (number & "length") | (number & "toString") | (number & "toLocaleString") | (number & "valueOf") | (number & "charAt") | (number & "charCodeAt") | (number & "concat") | (number & "indexOf") | (number & "lastIndexOf") | (number & "localeCompare") | (number & "match") | (number & "replace") | (number & "search") | (number & "slice") | (number & "split") | (number & "substring") | (number & "toLowerCase") | (number & "toLocaleLowerCase") | (number & "toUpperCase") | (number & "toLocaleUpperCase") | (number & "trim") | (number & "substr") | (number & "toFixed") | (number & "toExponential") | (number & "toPrecision") | ("length" & number) | ("charAt" & number) | ("charCodeAt" & number) | ("concat" & number) | ("indexOf" & number) | ("lastIndexOf" & number) | ("localeCompare" & number) | ("match" & number) | ("replace" & number) | ("search" & number) | ("slice" & number) | ("split" & number) | ("substring" & number) | ("toLowerCase" & number) | ("toLocaleLowerCase" & number) | ("toUpperCase" & number) | ("toLocaleUpperCase" & number) | ("trim" & number) | ("substr" & number) +>T04 : number | "length" | "toString" | "valueOf" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" >Object : Object type T05 = keyof keyof keyof keyof keyof Object; ->T05 : "toString" | "valueOf" | ("toString" & number) | ("toLocaleString" & number) | ("valueOf" & number) | ("toFixed" & number) | ("toExponential" & number) | ("toPrecision" & number) +>T05 : "toString" | "valueOf" >Object : Object type T06 = keyof keyof keyof keyof keyof keyof Object; ->T06 : number | "length" | "toString" | "valueOf" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | ("toString" & number) | ("valueOf" & number) | (number & "length") | (number & "toString") | (number & "toLocaleString") | (number & "valueOf") | (number & "charAt") | (number & "charCodeAt") | (number & "concat") | (number & "indexOf") | (number & "lastIndexOf") | (number & "localeCompare") | (number & "match") | (number & "replace") | (number & "search") | (number & "slice") | (number & "split") | (number & "substring") | (number & "toLowerCase") | (number & "toLocaleLowerCase") | (number & "toUpperCase") | (number & "toLocaleUpperCase") | (number & "trim") | (number & "substr") | (number & "toFixed") | (number & "toExponential") | (number & "toPrecision") | ("length" & number) | ("charAt" & number) | ("charCodeAt" & number) | ("concat" & number) | ("indexOf" & number) | ("lastIndexOf" & number) | ("localeCompare" & number) | ("match" & number) | ("replace" & number) | ("search" & number) | ("slice" & number) | ("split" & number) | ("substring" & number) | ("toLowerCase" & number) | ("toLocaleLowerCase" & number) | ("toUpperCase" & number) | ("toLocaleUpperCase" & number) | ("trim" & number) | ("substr" & number) +>T06 : number | "length" | "toString" | "valueOf" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" >Object : Object type T10 = Shape["name"]; diff --git a/tests/baselines/reference/switchCaseWithIntersectionTypes01.errors.txt b/tests/baselines/reference/switchCaseWithIntersectionTypes01.errors.txt index 3aca9d926b763..380e98c3c3ddf 100644 --- a/tests/baselines/reference/switchCaseWithIntersectionTypes01.errors.txt +++ b/tests/baselines/reference/switchCaseWithIntersectionTypes01.errors.txt @@ -1,10 +1,7 @@ -tests/cases/conformance/types/typeRelationships/comparable/switchCaseWithIntersectionTypes01.ts(18,10): error TS2678: Type '(number & true) | (number & false)' is not comparable to type 'string & number'. - Type 'number & false' is not comparable to type 'string & number'. - Type 'number & false' is not comparable to type 'string'. tests/cases/conformance/types/typeRelationships/comparable/switchCaseWithIntersectionTypes01.ts(22,10): error TS2678: Type 'boolean' is not comparable to type 'string & number'. -==== tests/cases/conformance/types/typeRelationships/comparable/switchCaseWithIntersectionTypes01.ts (2 errors) ==== +==== tests/cases/conformance/types/typeRelationships/comparable/switchCaseWithIntersectionTypes01.ts (1 errors) ==== var strAndNum: string & number; var numAndBool: number & boolean; var str: string; @@ -23,10 +20,6 @@ tests/cases/conformance/types/typeRelationships/comparable/switchCaseWithInterse // Overlap in constituents case numAndBool: - ~~~~~~~~~~ -!!! error TS2678: Type '(number & true) | (number & false)' is not comparable to type 'string & number'. -!!! error TS2678: Type 'number & false' is not comparable to type 'string & number'. -!!! error TS2678: Type 'number & false' is not comparable to type 'string'. break; // No relation diff --git a/tests/baselines/reference/switchCaseWithIntersectionTypes01.types b/tests/baselines/reference/switchCaseWithIntersectionTypes01.types index 4a578f223199d..9ba884985dc24 100644 --- a/tests/baselines/reference/switchCaseWithIntersectionTypes01.types +++ b/tests/baselines/reference/switchCaseWithIntersectionTypes01.types @@ -3,7 +3,7 @@ var strAndNum: string & number; >strAndNum : string & number var numAndBool: number & boolean; ->numAndBool : (number & true) | (number & false) +>numAndBool : never var str: string; >str : string @@ -34,7 +34,7 @@ switch (strAndNum) { // Overlap in constituents case numAndBool: ->numAndBool : (number & true) | (number & false) +>numAndBool : never break; diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.types b/tests/baselines/reference/typeGuardsWithInstanceOf.types index 7fce8cd45594e..3fd47f43fc303 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOf.types +++ b/tests/baselines/reference/typeGuardsWithInstanceOf.types @@ -25,7 +25,7 @@ if (!(result instanceof RegExp)) { } else if (!result.global) { >!result.global : boolean ->result.global : (string & true) | (string & false) +>result.global : never >result : I & RegExp ->global : (string & true) | (string & false) +>global : never } From 50c7ff79d0cfc44716df8c9ab512f5fff432a32d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 24 Apr 2018 15:57:17 -0700 Subject: [PATCH 4/9] Add tests --- .../intersectionWithUnionConstraint.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts diff --git a/tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts b/tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts new file mode 100644 index 0000000000000..b406dd915868a --- /dev/null +++ b/tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts @@ -0,0 +1,24 @@ +// @strict: true + +function f1(x: T & U) { + // Combined constraint of 'T & U' is 'string | number' + let y: string | number = x; +} + +function f2(x: T & U) { + let y1: string | number = x; // Error + let y2: string | null = x; // Error + let y3: string | undefined = x; + let y4: number | null = x; // Error + let y5: number | undefined = x; // Error + let y6: null | undefined = x; // Error +} + +type T1 = (string | number | undefined) & (string | null | undefined); // string | undefined + +// Repro from #23648 + +type Example = { [K in keyof T]: K extends keyof U ? UnexpectedError : NoErrorHere } + +type UnexpectedError = T +type NoErrorHere = T From b78054d9c3a76c64eca763bdaed5813b064e8fd2 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 24 Apr 2018 15:57:26 -0700 Subject: [PATCH 5/9] Accept new baselines --- ...intersectionWithUnionConstraint.errors.txt | 71 ++++++++++++++++ .../intersectionWithUnionConstraint.js | 39 +++++++++ .../intersectionWithUnionConstraint.symbols | 78 +++++++++++++++++ .../intersectionWithUnionConstraint.types | 83 +++++++++++++++++++ 4 files changed, 271 insertions(+) create mode 100644 tests/baselines/reference/intersectionWithUnionConstraint.errors.txt create mode 100644 tests/baselines/reference/intersectionWithUnionConstraint.js create mode 100644 tests/baselines/reference/intersectionWithUnionConstraint.symbols create mode 100644 tests/baselines/reference/intersectionWithUnionConstraint.types diff --git a/tests/baselines/reference/intersectionWithUnionConstraint.errors.txt b/tests/baselines/reference/intersectionWithUnionConstraint.errors.txt new file mode 100644 index 0000000000000..8892e41280550 --- /dev/null +++ b/tests/baselines/reference/intersectionWithUnionConstraint.errors.txt @@ -0,0 +1,71 @@ +tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(7,9): error TS2322: Type 'T & U' is not assignable to type 'string | number'. + Type 'string | undefined' is not assignable to type 'string | number'. + Type 'undefined' is not assignable to type 'string | number'. + Type 'T & U' is not assignable to type 'number'. +tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(8,9): error TS2322: Type 'T & U' is not assignable to type 'string | null'. + Type 'string | undefined' is not assignable to type 'string | null'. + Type 'undefined' is not assignable to type 'string | null'. + Type 'T & U' is not assignable to type 'string'. +tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(10,9): error TS2322: Type 'T & U' is not assignable to type 'number | null'. + Type 'string | undefined' is not assignable to type 'number | null'. + Type 'undefined' is not assignable to type 'number | null'. + Type 'T & U' is not assignable to type 'number'. +tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(11,9): error TS2322: Type 'T & U' is not assignable to type 'number | undefined'. + Type 'string | undefined' is not assignable to type 'number | undefined'. + Type 'string' is not assignable to type 'number | undefined'. + Type 'T & U' is not assignable to type 'number'. +tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(12,9): error TS2322: Type 'T & U' is not assignable to type 'null | undefined'. + Type 'string | undefined' is not assignable to type 'null | undefined'. + Type 'string' is not assignable to type 'null | undefined'. + Type 'T & U' is not assignable to type 'null'. + + +==== tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts (5 errors) ==== + function f1(x: T & U) { + // Combined constraint of 'T & U' is 'string | number' + let y: string | number = x; + } + + function f2(x: T & U) { + let y1: string | number = x; // Error + ~~ +!!! error TS2322: Type 'T & U' is not assignable to type 'string | number'. +!!! error TS2322: Type 'string | undefined' is not assignable to type 'string | number'. +!!! error TS2322: Type 'undefined' is not assignable to type 'string | number'. +!!! error TS2322: Type 'T & U' is not assignable to type 'number'. + let y2: string | null = x; // Error + ~~ +!!! error TS2322: Type 'T & U' is not assignable to type 'string | null'. +!!! error TS2322: Type 'string | undefined' is not assignable to type 'string | null'. +!!! error TS2322: Type 'undefined' is not assignable to type 'string | null'. +!!! error TS2322: Type 'T & U' is not assignable to type 'string'. + let y3: string | undefined = x; + let y4: number | null = x; // Error + ~~ +!!! error TS2322: Type 'T & U' is not assignable to type 'number | null'. +!!! error TS2322: Type 'string | undefined' is not assignable to type 'number | null'. +!!! error TS2322: Type 'undefined' is not assignable to type 'number | null'. +!!! error TS2322: Type 'T & U' is not assignable to type 'number'. + let y5: number | undefined = x; // Error + ~~ +!!! error TS2322: Type 'T & U' is not assignable to type 'number | undefined'. +!!! error TS2322: Type 'string | undefined' is not assignable to type 'number | undefined'. +!!! error TS2322: Type 'string' is not assignable to type 'number | undefined'. +!!! error TS2322: Type 'T & U' is not assignable to type 'number'. + let y6: null | undefined = x; // Error + ~~ +!!! error TS2322: Type 'T & U' is not assignable to type 'null | undefined'. +!!! error TS2322: Type 'string | undefined' is not assignable to type 'null | undefined'. +!!! error TS2322: Type 'string' is not assignable to type 'null | undefined'. +!!! error TS2322: Type 'T & U' is not assignable to type 'null'. + } + + type T1 = (string | number | undefined) & (string | null | undefined); // string | undefined + + // Repro from #23648 + + type Example = { [K in keyof T]: K extends keyof U ? UnexpectedError : NoErrorHere } + + type UnexpectedError = T + type NoErrorHere = T + \ No newline at end of file diff --git a/tests/baselines/reference/intersectionWithUnionConstraint.js b/tests/baselines/reference/intersectionWithUnionConstraint.js new file mode 100644 index 0000000000000..6069dbda75c89 --- /dev/null +++ b/tests/baselines/reference/intersectionWithUnionConstraint.js @@ -0,0 +1,39 @@ +//// [intersectionWithUnionConstraint.ts] +function f1(x: T & U) { + // Combined constraint of 'T & U' is 'string | number' + let y: string | number = x; +} + +function f2(x: T & U) { + let y1: string | number = x; // Error + let y2: string | null = x; // Error + let y3: string | undefined = x; + let y4: number | null = x; // Error + let y5: number | undefined = x; // Error + let y6: null | undefined = x; // Error +} + +type T1 = (string | number | undefined) & (string | null | undefined); // string | undefined + +// Repro from #23648 + +type Example = { [K in keyof T]: K extends keyof U ? UnexpectedError : NoErrorHere } + +type UnexpectedError = T +type NoErrorHere = T + + +//// [intersectionWithUnionConstraint.js] +"use strict"; +function f1(x) { + // Combined constraint of 'T & U' is 'string | number' + var y = x; +} +function f2(x) { + var y1 = x; // Error + var y2 = x; // Error + var y3 = x; + var y4 = x; // Error + var y5 = x; // Error + var y6 = x; // Error +} diff --git a/tests/baselines/reference/intersectionWithUnionConstraint.symbols b/tests/baselines/reference/intersectionWithUnionConstraint.symbols new file mode 100644 index 0000000000000..d7ee1e86a22b0 --- /dev/null +++ b/tests/baselines/reference/intersectionWithUnionConstraint.symbols @@ -0,0 +1,78 @@ +=== tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts === +function f1(x: T & U) { +>f1 : Symbol(f1, Decl(intersectionWithUnionConstraint.ts, 0, 0)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 0, 12)) +>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 0, 38)) +>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 0, 66)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 0, 12)) +>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 0, 38)) + + // Combined constraint of 'T & U' is 'string | number' + let y: string | number = x; +>y : Symbol(y, Decl(intersectionWithUnionConstraint.ts, 2, 7)) +>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 0, 66)) +} + +function f2(x: T & U) { +>f2 : Symbol(f2, Decl(intersectionWithUnionConstraint.ts, 3, 1)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 5, 12)) +>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 5, 50)) +>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 5, 88)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 5, 12)) +>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 5, 50)) + + let y1: string | number = x; // Error +>y1 : Symbol(y1, Decl(intersectionWithUnionConstraint.ts, 6, 7)) +>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 5, 88)) + + let y2: string | null = x; // Error +>y2 : Symbol(y2, Decl(intersectionWithUnionConstraint.ts, 7, 7)) +>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 5, 88)) + + let y3: string | undefined = x; +>y3 : Symbol(y3, Decl(intersectionWithUnionConstraint.ts, 8, 7)) +>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 5, 88)) + + let y4: number | null = x; // Error +>y4 : Symbol(y4, Decl(intersectionWithUnionConstraint.ts, 9, 7)) +>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 5, 88)) + + let y5: number | undefined = x; // Error +>y5 : Symbol(y5, Decl(intersectionWithUnionConstraint.ts, 10, 7)) +>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 5, 88)) + + let y6: null | undefined = x; // Error +>y6 : Symbol(y6, Decl(intersectionWithUnionConstraint.ts, 11, 7)) +>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 5, 88)) +} + +type T1 = (string | number | undefined) & (string | null | undefined); // string | undefined +>T1 : Symbol(T1, Decl(intersectionWithUnionConstraint.ts, 12, 1)) + +// Repro from #23648 + +type Example = { [K in keyof T]: K extends keyof U ? UnexpectedError : NoErrorHere } +>Example : Symbol(Example, Decl(intersectionWithUnionConstraint.ts, 14, 70)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 18, 13)) +>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 18, 15)) +>K : Symbol(K, Decl(intersectionWithUnionConstraint.ts, 18, 24)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 18, 13)) +>K : Symbol(K, Decl(intersectionWithUnionConstraint.ts, 18, 24)) +>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 18, 15)) +>UnexpectedError : Symbol(UnexpectedError, Decl(intersectionWithUnionConstraint.ts, 18, 96)) +>K : Symbol(K, Decl(intersectionWithUnionConstraint.ts, 18, 24)) +>NoErrorHere : Symbol(NoErrorHere, Decl(intersectionWithUnionConstraint.ts, 20, 47)) +>K : Symbol(K, Decl(intersectionWithUnionConstraint.ts, 18, 24)) + +type UnexpectedError = T +>UnexpectedError : Symbol(UnexpectedError, Decl(intersectionWithUnionConstraint.ts, 18, 96)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 20, 21)) +>PropertyKey : Symbol(PropertyKey, Decl(lib.d.ts, --, --)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 20, 21)) + +type NoErrorHere = T +>NoErrorHere : Symbol(NoErrorHere, Decl(intersectionWithUnionConstraint.ts, 20, 47)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 21, 17)) +>PropertyKey : Symbol(PropertyKey, Decl(lib.d.ts, --, --)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 21, 17)) + diff --git a/tests/baselines/reference/intersectionWithUnionConstraint.types b/tests/baselines/reference/intersectionWithUnionConstraint.types new file mode 100644 index 0000000000000..df5fa0f14d38b --- /dev/null +++ b/tests/baselines/reference/intersectionWithUnionConstraint.types @@ -0,0 +1,83 @@ +=== tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts === +function f1(x: T & U) { +>f1 : (x: T & U) => void +>T : T +>U : U +>x : T & U +>T : T +>U : U + + // Combined constraint of 'T & U' is 'string | number' + let y: string | number = x; +>y : string | number +>x : T & U +} + +function f2(x: T & U) { +>f2 : (x: T & U) => void +>T : T +>U : U +>null : null +>x : T & U +>T : T +>U : U + + let y1: string | number = x; // Error +>y1 : string | number +>x : T & U + + let y2: string | null = x; // Error +>y2 : string | null +>null : null +>x : T & U + + let y3: string | undefined = x; +>y3 : string | undefined +>x : T & U + + let y4: number | null = x; // Error +>y4 : number | null +>null : null +>x : T & U + + let y5: number | undefined = x; // Error +>y5 : number | undefined +>x : T & U + + let y6: null | undefined = x; // Error +>y6 : null | undefined +>null : null +>x : T & U +} + +type T1 = (string | number | undefined) & (string | null | undefined); // string | undefined +>T1 : string | undefined +>null : null + +// Repro from #23648 + +type Example = { [K in keyof T]: K extends keyof U ? UnexpectedError : NoErrorHere } +>Example : Example +>T : T +>U : U +>K : K +>T : T +>K : K +>U : U +>UnexpectedError : T +>K : K +>NoErrorHere : T +>K : K + +type UnexpectedError = T +>UnexpectedError : T +>T : T +>PropertyKey : string | number | symbol +>T : T + +type NoErrorHere = T +>NoErrorHere : T +>T : T +>PropertyKey : string | number | symbol +>T : T + From 3707f7dfbe56e408ee61fedc733c9bc35cfe2644 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 25 Apr 2018 10:58:08 -0700 Subject: [PATCH 6/9] Handle more situations involving combined constraints of intersections --- src/compiler/checker.ts | 85 ++++++++++++++++++++++++++--------------- src/compiler/types.ts | 2 + 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0618abe75bb4f..64669cda10eb6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6428,22 +6428,43 @@ namespace ts { return getConstraintOfDistributiveConditionalType(type) || getDefaultConstraintOfConditionalType(type); } - function getUnionConstraintOfIntersection(type: IntersectionType) { + function getUnionConstraintOfIntersection(type: IntersectionType, targetIsUnion: boolean) { let constraints: Type[]; + let hasDisjointDomainType = false; for (const t of type.types) { if (t.flags & TypeFlags.Instantiable) { - const baseConstraint = getBaseConstraintOfType(t); - if (!(baseConstraint.flags & TypeFlags.Union)) { - return undefined; + // We keep following constraints as long as we have an instantiable type that is known + // not to be circular or infinite (hence we stop on index access types). + let constraint = getConstraintOfType(t); + while (constraint && constraint.flags & (TypeFlags.TypeParameter | TypeFlags.Index | TypeFlags.Conditional)) { + constraint = getConstraintOfType(constraint); + } + if (constraint) { + // A constraint that isn't a union type implies that the final type would be a non-union + // type as well. Since non-union constraints are of no interest, we can exit here. + if (!(constraint.flags & TypeFlags.Union)) { + return undefined; + } + constraints = append(constraints, constraint); } - constraints = append(constraints, baseConstraint); + } + else if (t.flags & TypeFlags.DisjointDomains) { + hasDisjointDomainType = true; } } - if (constraints) { - const constraint = getIntersectionType(constraints); - if (constraint.flags & TypeFlags.Union) { - return constraint; + // If the target is a union type or if we are intersecting with types belonging to one of the + // disjoint domans, we may end up producing a constraint that hasn't been examined before. + if (constraints && (targetIsUnion || hasDisjointDomainType)) { + if (hasDisjointDomainType) { + // We add any types belong to one of the disjoint domans because they might cause the final + // intersection operation to reduce the union constraints. + for (const t of type.types) { + if (t.flags & TypeFlags.DisjointDomains) { + constraints = append(constraints, t); + } + } } + return getIntersectionType(constraints); } return undefined; } @@ -7920,10 +7941,11 @@ namespace ts { // Return true if the given intersection type contains // more than one unit type or, // an object type and a nullable type (null or undefined), or - // a string-like type and a non-string-like primitive type, or - // a number-like type and a non-number-like primitive type, or - // a symbol-like type and a non-symbol-like primitive type, or - // a void-like type and a non-void-like primitive type. + // a string-like type and a type known to be non-string-like, or + // a number-like type and a type known to be non-number-like, or + // a symbol-like type and a type known to be non-symbol-like, or + // a void-like type and a type known to be non-void-like, or + // a non-primitive type and a type known to be primitive. function isEmptyIntersectionType(type: IntersectionType) { let combined: TypeFlags = 0; for (const t of type.types) { @@ -7932,10 +7954,11 @@ namespace ts { } combined |= t.flags; if (combined & TypeFlags.Nullable && combined & (TypeFlags.Object | TypeFlags.NonPrimitive) || - combined & TypeFlags.StringLike && combined & (TypeFlags.Primitive & ~TypeFlags.StringLike) || - combined & TypeFlags.NumberLike && combined & (TypeFlags.Primitive & ~TypeFlags.NumberLike) || - combined & TypeFlags.ESSymbolLike && combined & (TypeFlags.Primitive & ~TypeFlags.ESSymbolLike) || - combined & TypeFlags.VoidLike && combined & (TypeFlags.Primitive & ~TypeFlags.VoidLike)) { + combined & TypeFlags.NonPrimitive && combined & (TypeFlags.DisjointDomains & ~TypeFlags.NonPrimitive) || + combined & TypeFlags.StringLike && combined & (TypeFlags.DisjointDomains & ~TypeFlags.StringLike) || + combined & TypeFlags.NumberLike && combined & (TypeFlags.DisjointDomains & ~TypeFlags.NumberLike) || + combined & TypeFlags.ESSymbolLike && combined & (TypeFlags.DisjointDomains & ~TypeFlags.ESSymbolLike) || + combined & TypeFlags.VoidLike && combined & (TypeFlags.DisjointDomains & ~TypeFlags.VoidLike)) { return true; } } @@ -10154,19 +10177,21 @@ namespace ts { errorInfo = saveErrorInfo; } } - if (!result && source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Union) { - // The combined constraint of an intersection type is the intersection of the constraints of - // the constituents. Since we break down union types before intersection types, if the target - // type is a union type and the source type is an intersection type with a combined constraint - // that is a union type, we need an extra check to see if the combined constraint is related to - // the target. For example, given two type variables T and U, each with the constraint - // 'string | number', the combined constraint of 'T & U' is 'string | number' and we need to - // check this constraint against the union on the target side. - const constraint = getUnionConstraintOfIntersection(source); - if (constraint) { - if (result = isRelatedTo(constraint, target, reportErrors)) { - errorInfo = saveErrorInfo; - } + } + if (!result && source.flags & TypeFlags.Intersection) { + // The combined constraint of an intersection type is the intersection of the constraints of + // the constituents. When an intersection type contains instantiable types with union type + // constraints, there are situations where we need to examine the combined constraint. One is + // when the target is a union type. Another is when the intersection contains types belonging + // to one of the disjoint domains. For example, given type variables T and U, each with the + // constraint 'string | number', the combined constraint of 'T & U' is 'string | number' and + // we need to check this constraint against a union on the target side. Also, given a type + // variable V constrained to 'string | number', 'V & number' has a combined constraint of + // 'string & number | number & number' which reduces to just 'number'. + const constraint = getUnionConstraintOfIntersection(source, !!(target.flags & TypeFlags.Union)); + if (constraint) { + if (result = isRelatedTo(constraint, target, reportErrors)) { + errorInfo = saveErrorInfo; } } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index fd3a58bc5ea55..06491357e5121 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3614,6 +3614,8 @@ namespace ts { EnumLike = Enum | EnumLiteral, ESSymbolLike = ESSymbol | UniqueESSymbol, VoidLike = Void | Undefined, + /* @internal */ + DisjointDomains = NonPrimitive | StringLike | NumberLike | BooleanLike | ESSymbolLike | VoidLike | Null, UnionOrIntersection = Union | Intersection, StructuredType = Object | Union | Intersection, TypeVariable = TypeParameter | IndexedAccess, From 8a7c2031fd76239b0e6f661d09210febb8dd9ef3 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 25 Apr 2018 10:58:20 -0700 Subject: [PATCH 7/9] Accept new baselines --- .../reference/keyofAndIndexedAccessErrors.errors.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt b/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt index bbd4685a4923d..c74c90c510f1b 100644 --- a/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt +++ b/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt @@ -44,6 +44,7 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(87,5): error tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(103,9): error TS2322: Type 'Extract' is not assignable to type 'K'. Type 'string & keyof T' is not assignable to type 'K'. Type 'string' is not assignable to type 'K'. + Type 'string' is not assignable to type 'K'. tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(105,9): error TS2322: Type 'T[Extract]' is not assignable to type 'T[K]'. Type 'Extract' is not assignable to type 'K'. tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(108,5): error TS2322: Type 'T[K]' is not assignable to type 'U[K]'. @@ -55,6 +56,7 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(114,5): error Type 'Extract' is not assignable to type 'J'. Type 'string & keyof T' is not assignable to type 'J'. Type 'string' is not assignable to type 'J'. + Type 'string' is not assignable to type 'J'. tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(117,5): error TS2322: Type 'T[K]' is not assignable to type 'U[J]'. Type 'T' is not assignable to type 'U'. @@ -238,6 +240,7 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(117,5): error !!! error TS2322: Type 'Extract' is not assignable to type 'K'. !!! error TS2322: Type 'string & keyof T' is not assignable to type 'K'. !!! error TS2322: Type 'string' is not assignable to type 'K'. +!!! error TS2322: Type 'string' is not assignable to type 'K'. t[key] = tk; // ok, T[K] ==> T[keyof T] tk = t[key]; // error, T[keyof T] =/=> T[K] ~~ @@ -264,6 +267,7 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(117,5): error !!! error TS2322: Type 'Extract' is not assignable to type 'J'. !!! error TS2322: Type 'string & keyof T' is not assignable to type 'J'. !!! error TS2322: Type 'string' is not assignable to type 'J'. +!!! error TS2322: Type 'string' is not assignable to type 'J'. tk = uj; uj = tk; // error From 8442a45bb6cd9f016dcd5aba0a17a10e38660de7 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 25 Apr 2018 11:03:25 -0700 Subject: [PATCH 8/9] Add additional tests --- .../intersection/intersectionWithUnionConstraint.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts b/tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts index b406dd915868a..278d72595cd65 100644 --- a/tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts +++ b/tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts @@ -16,6 +16,18 @@ function f2(x: T & (number | object | undefined)) { + const y: number | undefined = x; +} + +function f4(x: T & (number | object)) { + const y: number = x; +} + +function f5(x: keyof T & U) { + let y: keyof any = x; +} + // Repro from #23648 type Example = { [K in keyof T]: K extends keyof U ? UnexpectedError : NoErrorHere } From bbcb1bb25a6e74f956e8819a84fcf9ce3c897e79 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 25 Apr 2018 11:03:39 -0700 Subject: [PATCH 9/9] Accept new baselines --- ...intersectionWithUnionConstraint.errors.txt | 12 ++++ .../intersectionWithUnionConstraint.js | 21 ++++++ .../intersectionWithUnionConstraint.symbols | 70 ++++++++++++++----- .../intersectionWithUnionConstraint.types | 36 ++++++++++ 4 files changed, 122 insertions(+), 17 deletions(-) diff --git a/tests/baselines/reference/intersectionWithUnionConstraint.errors.txt b/tests/baselines/reference/intersectionWithUnionConstraint.errors.txt index 8892e41280550..9bdfc6f30f9ad 100644 --- a/tests/baselines/reference/intersectionWithUnionConstraint.errors.txt +++ b/tests/baselines/reference/intersectionWithUnionConstraint.errors.txt @@ -62,6 +62,18 @@ tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(12 type T1 = (string | number | undefined) & (string | null | undefined); // string | undefined + function f3(x: T & (number | object | undefined)) { + const y: number | undefined = x; + } + + function f4(x: T & (number | object)) { + const y: number = x; + } + + function f5(x: keyof T & U) { + let y: keyof any = x; + } + // Repro from #23648 type Example = { [K in keyof T]: K extends keyof U ? UnexpectedError : NoErrorHere } diff --git a/tests/baselines/reference/intersectionWithUnionConstraint.js b/tests/baselines/reference/intersectionWithUnionConstraint.js index 6069dbda75c89..fc3e50309c6f5 100644 --- a/tests/baselines/reference/intersectionWithUnionConstraint.js +++ b/tests/baselines/reference/intersectionWithUnionConstraint.js @@ -15,6 +15,18 @@ function f2(x: T & (number | object | undefined)) { + const y: number | undefined = x; +} + +function f4(x: T & (number | object)) { + const y: number = x; +} + +function f5(x: keyof T & U) { + let y: keyof any = x; +} + // Repro from #23648 type Example = { [K in keyof T]: K extends keyof U ? UnexpectedError : NoErrorHere } @@ -37,3 +49,12 @@ function f2(x) { var y5 = x; // Error var y6 = x; // Error } +function f3(x) { + var y = x; +} +function f4(x) { + var y = x; +} +function f5(x) { + var y = x; +} diff --git a/tests/baselines/reference/intersectionWithUnionConstraint.symbols b/tests/baselines/reference/intersectionWithUnionConstraint.symbols index d7ee1e86a22b0..0b7d7455977ca 100644 --- a/tests/baselines/reference/intersectionWithUnionConstraint.symbols +++ b/tests/baselines/reference/intersectionWithUnionConstraint.symbols @@ -49,30 +49,66 @@ function f2T1 : Symbol(T1, Decl(intersectionWithUnionConstraint.ts, 12, 1)) +function f3(x: T & (number | object | undefined)) { +>f3 : Symbol(f3, Decl(intersectionWithUnionConstraint.ts, 14, 70)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 16, 12)) +>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 16, 51)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 16, 12)) + + const y: number | undefined = x; +>y : Symbol(y, Decl(intersectionWithUnionConstraint.ts, 17, 9)) +>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 16, 51)) +} + +function f4(x: T & (number | object)) { +>f4 : Symbol(f4, Decl(intersectionWithUnionConstraint.ts, 18, 1)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 20, 12)) +>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 20, 39)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 20, 12)) + + const y: number = x; +>y : Symbol(y, Decl(intersectionWithUnionConstraint.ts, 21, 9)) +>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 20, 39)) +} + +function f5(x: keyof T & U) { +>f5 : Symbol(f5, Decl(intersectionWithUnionConstraint.ts, 22, 1)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 24, 12)) +>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 24, 14)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 24, 12)) +>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 24, 34)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 24, 12)) +>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 24, 14)) + + let y: keyof any = x; +>y : Symbol(y, Decl(intersectionWithUnionConstraint.ts, 25, 7)) +>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 24, 34)) +} + // Repro from #23648 type Example = { [K in keyof T]: K extends keyof U ? UnexpectedError : NoErrorHere } ->Example : Symbol(Example, Decl(intersectionWithUnionConstraint.ts, 14, 70)) ->T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 18, 13)) ->U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 18, 15)) ->K : Symbol(K, Decl(intersectionWithUnionConstraint.ts, 18, 24)) ->T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 18, 13)) ->K : Symbol(K, Decl(intersectionWithUnionConstraint.ts, 18, 24)) ->U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 18, 15)) ->UnexpectedError : Symbol(UnexpectedError, Decl(intersectionWithUnionConstraint.ts, 18, 96)) ->K : Symbol(K, Decl(intersectionWithUnionConstraint.ts, 18, 24)) ->NoErrorHere : Symbol(NoErrorHere, Decl(intersectionWithUnionConstraint.ts, 20, 47)) ->K : Symbol(K, Decl(intersectionWithUnionConstraint.ts, 18, 24)) +>Example : Symbol(Example, Decl(intersectionWithUnionConstraint.ts, 26, 1)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 30, 13)) +>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 30, 15)) +>K : Symbol(K, Decl(intersectionWithUnionConstraint.ts, 30, 24)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 30, 13)) +>K : Symbol(K, Decl(intersectionWithUnionConstraint.ts, 30, 24)) +>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 30, 15)) +>UnexpectedError : Symbol(UnexpectedError, Decl(intersectionWithUnionConstraint.ts, 30, 96)) +>K : Symbol(K, Decl(intersectionWithUnionConstraint.ts, 30, 24)) +>NoErrorHere : Symbol(NoErrorHere, Decl(intersectionWithUnionConstraint.ts, 32, 47)) +>K : Symbol(K, Decl(intersectionWithUnionConstraint.ts, 30, 24)) type UnexpectedError = T ->UnexpectedError : Symbol(UnexpectedError, Decl(intersectionWithUnionConstraint.ts, 18, 96)) ->T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 20, 21)) +>UnexpectedError : Symbol(UnexpectedError, Decl(intersectionWithUnionConstraint.ts, 30, 96)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 32, 21)) >PropertyKey : Symbol(PropertyKey, Decl(lib.d.ts, --, --)) ->T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 20, 21)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 32, 21)) type NoErrorHere = T ->NoErrorHere : Symbol(NoErrorHere, Decl(intersectionWithUnionConstraint.ts, 20, 47)) ->T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 21, 17)) +>NoErrorHere : Symbol(NoErrorHere, Decl(intersectionWithUnionConstraint.ts, 32, 47)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 33, 17)) >PropertyKey : Symbol(PropertyKey, Decl(lib.d.ts, --, --)) ->T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 21, 17)) +>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 33, 17)) diff --git a/tests/baselines/reference/intersectionWithUnionConstraint.types b/tests/baselines/reference/intersectionWithUnionConstraint.types index df5fa0f14d38b..bef278feba1f2 100644 --- a/tests/baselines/reference/intersectionWithUnionConstraint.types +++ b/tests/baselines/reference/intersectionWithUnionConstraint.types @@ -54,6 +54,42 @@ type T1 = (string | number | undefined) & (string | null | undefined); // strin >T1 : string | undefined >null : null +function f3(x: T & (number | object | undefined)) { +>f3 : (x: (T & undefined) | (T & number) | (T & object)) => void +>T : T +>x : (T & undefined) | (T & number) | (T & object) +>T : T + + const y: number | undefined = x; +>y : number | undefined +>x : (T & undefined) | (T & number) | (T & object) +} + +function f4(x: T & (number | object)) { +>f4 : (x: (T & number) | (T & object)) => void +>T : T +>x : (T & number) | (T & object) +>T : T + + const y: number = x; +>y : number +>x : (T & number) | (T & object) +} + +function f5(x: keyof T & U) { +>f5 : (x: keyof T & U) => void +>T : T +>U : U +>T : T +>x : keyof T & U +>T : T +>U : U + + let y: keyof any = x; +>y : string | number | symbol +>x : keyof T & U +} + // Repro from #23648 type Example = { [K in keyof T]: K extends keyof U ? UnexpectedError : NoErrorHere }