diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 217eb47cfa627..ed75651e6e05f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18207,13 +18207,12 @@ namespace ts { let result = Ternary.False; const saveErrorInfo = captureErrorCalculationState(); - // Note that these checks are specifically ordered to produce correct results. In particular, - // we need to deconstruct unions before intersections (because unions are always at the top), - // and we need to handle "each" relations before "some" relations for the same kind of type. - if (source.flags & TypeFlags.UnionOrIntersection || target.flags & TypeFlags.UnionOrIntersection) { - result = getConstituentCount(source) * getConstituentCount(target) >= 4 ? - recursiveTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck, recursionFlags) : - structuredTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck); + if ((source.flags & TypeFlags.Union || target.flags & TypeFlags.Union) && getConstituentCount(source) * getConstituentCount(target) < 4) { + // We skip caching when source or target is a union with no more than three constituents. + result = structuredTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck); + } + else if (source.flags & TypeFlags.UnionOrIntersection || target.flags & TypeFlags.UnionOrIntersection) { + result = recursiveTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck, recursionFlags); } if (!result && !(source.flags & TypeFlags.Union) && (source.flags & (TypeFlags.StructuredOrInstantiable) || target.flags & TypeFlags.StructuredOrInstantiable)) { if (result = recursiveTypeRelatedTo(source, target, reportErrors, intersectionState, recursionFlags)) { @@ -18677,17 +18676,17 @@ namespace ts { const maybeStart = maybeCount; maybeKeys[maybeCount] = id; maybeCount++; + const saveExpandingFlags = expandingFlags; if (recursionFlags & RecursionFlags.Source) { sourceStack[sourceDepth] = source; sourceDepth++; + if (!(expandingFlags & ExpandingFlags.Source) && isDeeplyNestedType(source, sourceStack, sourceDepth)) expandingFlags |= ExpandingFlags.Source; } if (recursionFlags & RecursionFlags.Target) { targetStack[targetDepth] = target; targetDepth++; + if (!(expandingFlags & ExpandingFlags.Target) && isDeeplyNestedType(target, targetStack, targetDepth)) expandingFlags |= ExpandingFlags.Target; } - const saveExpandingFlags = expandingFlags; - if (!(expandingFlags & ExpandingFlags.Source) && isDeeplyNestedType(source, sourceStack, sourceDepth)) expandingFlags |= ExpandingFlags.Source; - if (!(expandingFlags & ExpandingFlags.Target) && isDeeplyNestedType(target, targetStack, targetDepth)) expandingFlags |= ExpandingFlags.Target; let originalHandler: typeof outofbandVarianceMarkerHandler; let propagatingVarianceFlags: RelationComparisonResult = 0; if (outofbandVarianceMarkerHandler) { @@ -18713,13 +18712,13 @@ namespace ts { if (outofbandVarianceMarkerHandler) { outofbandVarianceMarkerHandler = originalHandler; } - expandingFlags = saveExpandingFlags; if (recursionFlags & RecursionFlags.Source) { sourceDepth--; } if (recursionFlags & RecursionFlags.Target) { targetDepth--; } + expandingFlags = saveExpandingFlags; if (result) { if (result === Ternary.True || (sourceDepth === 0 && targetDepth === 0)) { if (result === Ternary.True || result === Ternary.Maybe) { @@ -23150,7 +23149,7 @@ namespace ts { } function getConstituentCount(type: Type) { - return type.flags & TypeFlags.UnionOrIntersection ? (type as UnionOrIntersectionType).types.length : 1; + return type.flags & TypeFlags.Union ? (type as UnionType).types.length : 1; } function extractTypesOfKind(type: Type, kind: TypeFlags) { diff --git a/tests/baselines/reference/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.errors.txt b/tests/baselines/reference/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.errors.txt index af63c18523d6a..ec5fce9a3825c 100644 --- a/tests/baselines/reference/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.errors.txt +++ b/tests/baselines/reference/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.errors.txt @@ -9,16 +9,14 @@ tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.t Type '"text" | "email"' is not assignable to type 'ChannelOfType["type"]'. Type '"text"' is not assignable to type 'ChannelOfType["type"]'. Type '"text"' is not assignable to type 'T & "text"'. - Type '"text"' is not assignable to type 'T'. - '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'. - Type 'T' is not assignable to type 'T & "text"'. - Type '"text" | "email"' is not assignable to type 'T & "text"'. - Type '"text"' is not assignable to type 'T & "text"'. - Type '"text"' is not assignable to type 'T'. - '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'. - Type 'T' is not assignable to type '"text"'. - Type '"text" | "email"' is not assignable to type '"text"'. - Type '"email"' is not assignable to type '"text"'. + Type 'T' is not assignable to type 'T & "text"'. + Type '"text" | "email"' is not assignable to type 'T & "text"'. + Type '"text"' is not assignable to type 'T & "text"'. + Type '"text"' is not assignable to type 'T'. + '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'. + Type 'T' is not assignable to type '"text"'. + Type '"text" | "email"' is not assignable to type '"text"'. + Type '"email"' is not assignable to type '"text"'. ==== tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts (1 errors) ==== @@ -67,16 +65,14 @@ tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.t !!! error TS2322: Type '"text" | "email"' is not assignable to type 'ChannelOfType["type"]'. !!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType["type"]'. !!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'. -!!! error TS2322: Type '"text"' is not assignable to type 'T'. -!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'. -!!! error TS2322: Type 'T' is not assignable to type 'T & "text"'. -!!! error TS2322: Type '"text" | "email"' is not assignable to type 'T & "text"'. -!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'. -!!! error TS2322: Type '"text"' is not assignable to type 'T'. -!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'. -!!! error TS2322: Type 'T' is not assignable to type '"text"'. -!!! error TS2322: Type '"text" | "email"' is not assignable to type '"text"'. -!!! error TS2322: Type '"email"' is not assignable to type '"text"'. +!!! error TS2322: Type 'T' is not assignable to type 'T & "text"'. +!!! error TS2322: Type '"text" | "email"' is not assignable to type 'T & "text"'. +!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'. +!!! error TS2322: Type '"text"' is not assignable to type 'T'. +!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'. +!!! error TS2322: Type 'T' is not assignable to type '"text"'. +!!! error TS2322: Type '"text" | "email"' is not assignable to type '"text"'. +!!! error TS2322: Type '"email"' is not assignable to type '"text"'. } const newTextChannel = makeNewChannel('text'); diff --git a/tests/baselines/reference/deepComparisons.errors.txt b/tests/baselines/reference/deepComparisons.errors.txt index 43eec648b7fdb..663d5d80d1b9f 100644 --- a/tests/baselines/reference/deepComparisons.errors.txt +++ b/tests/baselines/reference/deepComparisons.errors.txt @@ -46,4 +46,23 @@ tests/cases/compiler/deepComparisons.ts(4,9): error TS2322: Type 'T[K1][K2]' is function f3() { let x: Foo1 = 0 as any as Bar; // No error! - } \ No newline at end of file + } + + // Repro from #46500 + + type F = {} & ( + T extends [any, ...any[]] + ? { [K in keyof T]?: F } + : T extends any[] + ? F[] + : T extends { [K: string]: any } + ? { [K in keyof T]?: F } + : { x: string } + ); + + declare function f(): F; + + function g() { + return f() as F; + } + \ No newline at end of file diff --git a/tests/baselines/reference/deepComparisons.js b/tests/baselines/reference/deepComparisons.js index 95d81ea96cf41..78bd274047ada 100644 --- a/tests/baselines/reference/deepComparisons.js +++ b/tests/baselines/reference/deepComparisons.js @@ -17,7 +17,26 @@ type Foo2 = { x: Foo1 }; function f3() { let x: Foo1 = 0 as any as Bar; // No error! -} +} + +// Repro from #46500 + +type F = {} & ( + T extends [any, ...any[]] + ? { [K in keyof T]?: F } + : T extends any[] + ? F[] + : T extends { [K: string]: any } + ? { [K in keyof T]?: F } + : { x: string } +); + +declare function f(): F; + +function g() { + return f() as F; +} + //// [deepComparisons.js] function f1() { @@ -31,3 +50,6 @@ function f2() { function f3() { var x = 0; // No error! } +function g() { + return f(); +} diff --git a/tests/baselines/reference/deepComparisons.symbols b/tests/baselines/reference/deepComparisons.symbols index 4c172345e07ac..7b2dbd596ebfd 100644 --- a/tests/baselines/reference/deepComparisons.symbols +++ b/tests/baselines/reference/deepComparisons.symbols @@ -84,3 +84,57 @@ function f3() { >Bar : Symbol(Bar, Decl(deepComparisons.ts, 6, 28)) >U : Symbol(U, Decl(deepComparisons.ts, 16, 12)) } + +// Repro from #46500 + +type F = {} & ( +>F : Symbol(F, Decl(deepComparisons.ts, 18, 1)) +>T : Symbol(T, Decl(deepComparisons.ts, 22, 7)) + + T extends [any, ...any[]] +>T : Symbol(T, Decl(deepComparisons.ts, 22, 7)) + + ? { [K in keyof T]?: F } +>K : Symbol(K, Decl(deepComparisons.ts, 24, 13)) +>T : Symbol(T, Decl(deepComparisons.ts, 22, 7)) +>F : Symbol(F, Decl(deepComparisons.ts, 18, 1)) +>T : Symbol(T, Decl(deepComparisons.ts, 22, 7)) +>K : Symbol(K, Decl(deepComparisons.ts, 24, 13)) + + : T extends any[] +>T : Symbol(T, Decl(deepComparisons.ts, 22, 7)) + + ? F[] +>F : Symbol(F, Decl(deepComparisons.ts, 18, 1)) +>T : Symbol(T, Decl(deepComparisons.ts, 22, 7)) + + : T extends { [K: string]: any } +>T : Symbol(T, Decl(deepComparisons.ts, 22, 7)) +>K : Symbol(K, Decl(deepComparisons.ts, 27, 27)) + + ? { [K in keyof T]?: F } +>K : Symbol(K, Decl(deepComparisons.ts, 28, 21)) +>T : Symbol(T, Decl(deepComparisons.ts, 22, 7)) +>F : Symbol(F, Decl(deepComparisons.ts, 18, 1)) +>T : Symbol(T, Decl(deepComparisons.ts, 22, 7)) +>K : Symbol(K, Decl(deepComparisons.ts, 28, 21)) + + : { x: string } +>x : Symbol(x, Decl(deepComparisons.ts, 29, 19)) + +); + +declare function f(): F; +>f : Symbol(f, Decl(deepComparisons.ts, 30, 2)) +>T : Symbol(T, Decl(deepComparisons.ts, 32, 19)) +>F : Symbol(F, Decl(deepComparisons.ts, 18, 1)) +>T : Symbol(T, Decl(deepComparisons.ts, 32, 19)) + +function g() { +>g : Symbol(g, Decl(deepComparisons.ts, 32, 36)) + + return f() as F; +>f : Symbol(f, Decl(deepComparisons.ts, 30, 2)) +>F : Symbol(F, Decl(deepComparisons.ts, 18, 1)) +} + diff --git a/tests/baselines/reference/deepComparisons.types b/tests/baselines/reference/deepComparisons.types index 7bc16b70f8a64..f7cbbfae8a043 100644 --- a/tests/baselines/reference/deepComparisons.types +++ b/tests/baselines/reference/deepComparisons.types @@ -56,3 +56,34 @@ function f3() { >0 as any : any >0 : 0 } + +// Repro from #46500 + +type F = {} & ( +>F : F + + T extends [any, ...any[]] + ? { [K in keyof T]?: F } + : T extends any[] + ? F[] + : T extends { [K: string]: any } +>K : string + + ? { [K in keyof T]?: F } + : { x: string } +>x : string + +); + +declare function f(): F; +>f : () => F + +function g() { +>g : () => F + + return f() as F; +>f() as F : F +>f() : F +>f : () => F +} + diff --git a/tests/baselines/reference/intersectionAndUnionTypes.errors.txt b/tests/baselines/reference/intersectionAndUnionTypes.errors.txt index e700e7c472109..f2d810fb0af10 100644 --- a/tests/baselines/reference/intersectionAndUnionTypes.errors.txt +++ b/tests/baselines/reference/intersectionAndUnionTypes.errors.txt @@ -5,7 +5,6 @@ tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(20,1): e tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(23,1): error TS2322: Type 'A | B' is not assignable to type '(A & B) | (C & D)'. Type 'A' is not assignable to type '(A & B) | (C & D)'. Type 'A' is not assignable to type 'A & B'. - Type 'A' is not assignable to type 'B'. tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(25,1): error TS2322: Type 'C | D' is not assignable to type '(A & B) | (C & D)'. Type 'C' is not assignable to type '(A & B) | (C & D)'. Type 'C' is not assignable to type 'C & D'. @@ -80,7 +79,6 @@ tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(37,1): e !!! error TS2322: Type 'A | B' is not assignable to type '(A & B) | (C & D)'. !!! error TS2322: Type 'A' is not assignable to type '(A & B) | (C & D)'. !!! error TS2322: Type 'A' is not assignable to type 'A & B'. -!!! error TS2322: Type 'A' is not assignable to type 'B'. x = cnd; // Ok x = cod; ~ diff --git a/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt b/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt index ba319beca3236..b629598156274 100644 --- a/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt +++ b/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt @@ -33,8 +33,11 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(82,5): error Type 'string | number | symbol' is not assignable to type 'keyof U'. Type 'string' is not assignable to type 'keyof U'. tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(83,5): error TS2322: Type 'keyof T | keyof U' is not assignable to type 'keyof T & keyof U'. + Type 'keyof T' is not assignable to type 'keyof T & keyof U'. tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(86,5): error TS2322: Type 'keyof T | keyof U' is not assignable to type 'keyof T & keyof U'. + Type 'keyof T' is not assignable to type 'keyof T & keyof U'. tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(87,5): error TS2322: Type 'keyof T | keyof U' is not assignable to type 'keyof T & keyof U'. + Type 'keyof T' is not assignable to type 'keyof T & keyof U'. tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(103,9): error TS2322: Type 'Extract' is not assignable to type 'K'. 'Extract' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'. Type 'string & keyof T' is not assignable to type 'K'. @@ -209,14 +212,17 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(142,5): error k1 = k4; // Error ~~ !!! error TS2322: Type 'keyof T | keyof U' is not assignable to type 'keyof T & keyof U'. +!!! error TS2322: Type 'keyof T' is not assignable to type 'keyof T & keyof U'. k2 = k1; k2 = k3; // Error ~~ !!! error TS2322: Type 'keyof T | keyof U' is not assignable to type 'keyof T & keyof U'. +!!! error TS2322: Type 'keyof T' is not assignable to type 'keyof T & keyof U'. k2 = k4; // Error ~~ !!! error TS2322: Type 'keyof T | keyof U' is not assignable to type 'keyof T & keyof U'. +!!! error TS2322: Type 'keyof T' is not assignable to type 'keyof T & keyof U'. k3 = k1; k3 = k2; diff --git a/tests/baselines/reference/unionTypeCallSignatures6.errors.txt b/tests/baselines/reference/unionTypeCallSignatures6.errors.txt index 51b18b204a773..2d162fc354d58 100644 --- a/tests/baselines/reference/unionTypeCallSignatures6.errors.txt +++ b/tests/baselines/reference/unionTypeCallSignatures6.errors.txt @@ -6,14 +6,12 @@ tests/cases/conformance/types/union/unionTypeCallSignatures6.ts(38,4): error TS2 tests/cases/conformance/types/union/unionTypeCallSignatures6.ts(39,1): error TS2684: The 'this' context of type 'A & C & { f0: F0 | F3; f1: F1 | F3; f2: F1 | F4; f3: F3 | F4; f4: F3 | F5; }' is not assignable to method's 'this' of type 'B'. Property 'b' is missing in type 'A & C & { f0: F0 | F3; f1: F1 | F3; f2: F1 | F4; f3: F3 | F4; f4: F3 | F5; }' but required in type 'B'. tests/cases/conformance/types/union/unionTypeCallSignatures6.ts(48,1): error TS2684: The 'this' context of type 'void' is not assignable to method's 'this' of type 'A & B'. - Type 'void' is not assignable to type 'A'. tests/cases/conformance/types/union/unionTypeCallSignatures6.ts(55,1): error TS2769: No overload matches this call. Overload 1 of 2, '(this: A & B & C): void', gave the following error. The 'this' context of type 'void' is not assignable to method's 'this' of type 'A & B & C'. Type 'void' is not assignable to type 'A'. Overload 2 of 2, '(this: A & B): void', gave the following error. The 'this' context of type 'void' is not assignable to method's 'this' of type 'A & B'. - Type 'void' is not assignable to type 'A'. ==== tests/cases/conformance/types/union/unionTypeCallSignatures6.ts (6 errors) ==== @@ -79,7 +77,6 @@ tests/cases/conformance/types/union/unionTypeCallSignatures6.ts(55,1): error TS2 f3(); // error ~~~~ !!! error TS2684: The 'this' context of type 'void' is not assignable to method's 'this' of type 'A & B'. -!!! error TS2684: Type 'void' is not assignable to type 'A'. interface F7 { (this: A & B & C): void; @@ -94,5 +91,4 @@ tests/cases/conformance/types/union/unionTypeCallSignatures6.ts(55,1): error TS2 !!! error TS2769: Type 'void' is not assignable to type 'A'. !!! error TS2769: Overload 2 of 2, '(this: A & B): void', gave the following error. !!! error TS2769: The 'this' context of type 'void' is not assignable to method's 'this' of type 'A & B'. -!!! error TS2769: Type 'void' is not assignable to type 'A'. \ No newline at end of file diff --git a/tests/cases/compiler/deepComparisons.ts b/tests/cases/compiler/deepComparisons.ts index 1323673945a58..a14fafc8ffeb3 100644 --- a/tests/cases/compiler/deepComparisons.ts +++ b/tests/cases/compiler/deepComparisons.ts @@ -16,4 +16,22 @@ type Foo2 = { x: Foo1 }; function f3() { let x: Foo1 = 0 as any as Bar; // No error! -} \ No newline at end of file +} + +// Repro from #46500 + +type F = {} & ( + T extends [any, ...any[]] + ? { [K in keyof T]?: F } + : T extends any[] + ? F[] + : T extends { [K: string]: any } + ? { [K in keyof T]?: F } + : { x: string } +); + +declare function f(): F; + +function g() { + return f() as F; +}