Skip to content

Commit 28050d5

Browse files
TypeScript Botsandersn
TypeScript Bot
authored andcommitted
Cherry-pick PR #34789 into release-3.7 (#34812)
Component commits: 2e0b451 Add isIntersectionConstituent to relation key isIntersectionConstituent controls whether relation checking performs excess property and common property checks. It is possible to fail a relation check with excess property checks turned on, cache the result, and then skip a relation check with excess property checks that would have succeeded. #33133 provides an example of such a program. Fixes #33133 the right way, so I reverted the fix at #33213 Fixes #34762 (by reverting #33213) Fixes #33944 -- I added the test from #34646 14d7a44 Merge branch 'master' into add-isIntersectionConstituent-to-relation-key ea80362 Update comments in test 0764275 Merge branch 'master' into add-isIntersectionConstituent-to-relation-key
1 parent 7d77ecb commit 28050d5

14 files changed

+209
-45
lines changed

src/compiler/checker.ts

+10-9
Original file line numberDiff line numberDiff line change
@@ -14160,7 +14160,7 @@ namespace ts {
1416014160
return true;
1416114161
}
1416214162
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
14163-
const related = relation.get(getRelationKey(source, target, relation));
14163+
const related = relation.get(getRelationKey(source, target, /*isIntersectionConstituent*/ false, relation));
1416414164
if (related !== undefined) {
1416514165
return !!(related & RelationComparisonResult.Succeeded);
1416614166
}
@@ -14566,7 +14566,7 @@ namespace ts {
1456614566
// and we need to handle "each" relations before "some" relations for the same kind of type.
1456714567
if (source.flags & TypeFlags.Union) {
1456814568
result = relation === comparableRelation ?
14569-
someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), isIntersectionConstituent) :
14569+
someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive)) :
1457014570
eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive));
1457114571
}
1457214572
else {
@@ -14604,7 +14604,7 @@ namespace ts {
1460414604
//
1460514605
// - For a primitive type or type parameter (such as 'number = A & B') there is no point in
1460614606
// breaking the intersection apart.
14607-
result = someTypeRelatedToType(<IntersectionType>source, target, /*reportErrors*/ false, /*isIntersectionConstituent*/ true);
14607+
result = someTypeRelatedToType(<IntersectionType>source, target, /*reportErrors*/ false);
1460814608
}
1460914609
if (!result && (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable)) {
1461014610
if (result = recursiveTypeRelatedTo(source, target, reportErrors, isIntersectionConstituent)) {
@@ -14898,14 +14898,14 @@ namespace ts {
1489814898
return result;
1489914899
}
1490014900

14901-
function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary {
14901+
function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary {
1490214902
const sourceTypes = source.types;
1490314903
if (source.flags & TypeFlags.Union && containsType(sourceTypes, target)) {
1490414904
return Ternary.True;
1490514905
}
1490614906
const len = sourceTypes.length;
1490714907
for (let i = 0; i < len; i++) {
14908-
const related = isRelatedTo(sourceTypes[i], target, reportErrors && i === len - 1, /*headMessage*/ undefined, isIntersectionConstituent);
14908+
const related = isRelatedTo(sourceTypes[i], target, reportErrors && i === len - 1);
1490914909
if (related) {
1491014910
return related;
1491114911
}
@@ -14992,7 +14992,7 @@ namespace ts {
1499214992
if (overflow) {
1499314993
return Ternary.False;
1499414994
}
14995-
const id = getRelationKey(source, target, relation);
14995+
const id = getRelationKey(source, target, isIntersectionConstituent, relation);
1499614996
const entry = relation.get(id);
1499714997
if (entry !== undefined) {
1499814998
if (reportErrors && entry & RelationComparisonResult.Failed && !(entry & RelationComparisonResult.Reported)) {
@@ -16206,17 +16206,18 @@ namespace ts {
1620616206
* To improve caching, the relation key for two generic types uses the target's id plus ids of the type parameters.
1620716207
* For other cases, the types ids are used.
1620816208
*/
16209-
function getRelationKey(source: Type, target: Type, relation: Map<RelationComparisonResult>) {
16209+
function getRelationKey(source: Type, target: Type, isIntersectionConstituent: boolean, relation: Map<RelationComparisonResult>) {
1621016210
if (relation === identityRelation && source.id > target.id) {
1621116211
const temp = source;
1621216212
source = target;
1621316213
target = temp;
1621416214
}
16215+
const intersection = isIntersectionConstituent ? "&" : "";
1621516216
if (isTypeReferenceWithGenericArguments(source) && isTypeReferenceWithGenericArguments(target)) {
1621616217
const typeParameters: Type[] = [];
16217-
return getTypeReferenceId(<TypeReference>source, typeParameters) + "," + getTypeReferenceId(<TypeReference>target, typeParameters);
16218+
return getTypeReferenceId(<TypeReference>source, typeParameters) + "," + getTypeReferenceId(<TypeReference>target, typeParameters) + intersection;
1621816219
}
16219-
return source.id + "," + target.id;
16220+
return source.id + "," + target.id + intersection;
1622016221
}
1622116222

1622216223
// Invoke the callback for each underlying property symbol of the given symbol and return the first
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
tests/cases/conformance/types/intersection/commonTypeIntersection.ts(2,5): error TS2322: Type '{ __typename?: "TypeTwo"; } & { a: boolean; }' is not assignable to type '{ __typename?: "TypeOne"; } & { a: boolean; }'.
2+
Type '{ __typename?: "TypeTwo"; } & { a: boolean; }' is not assignable to type '{ __typename?: "TypeOne"; }'.
3+
Types of property '__typename' are incompatible.
4+
Type '"TypeTwo"' is not assignable to type '"TypeOne"'.
5+
tests/cases/conformance/types/intersection/commonTypeIntersection.ts(4,5): error TS2322: Type '{ __typename?: "TypeTwo"; } & string' is not assignable to type '{ __typename?: "TypeOne"; } & string'.
6+
Type '{ __typename?: "TypeTwo"; } & string' is not assignable to type '{ __typename?: "TypeOne"; }'.
7+
Types of property '__typename' are incompatible.
8+
Type '"TypeTwo"' is not assignable to type '"TypeOne"'.
9+
10+
11+
==== tests/cases/conformance/types/intersection/commonTypeIntersection.ts (2 errors) ====
12+
declare let x1: { __typename?: 'TypeTwo' } & { a: boolean };
13+
let y1: { __typename?: 'TypeOne' } & { a: boolean} = x1; // should error here
14+
~~
15+
!!! error TS2322: Type '{ __typename?: "TypeTwo"; } & { a: boolean; }' is not assignable to type '{ __typename?: "TypeOne"; } & { a: boolean; }'.
16+
!!! error TS2322: Type '{ __typename?: "TypeTwo"; } & { a: boolean; }' is not assignable to type '{ __typename?: "TypeOne"; }'.
17+
!!! error TS2322: Types of property '__typename' are incompatible.
18+
!!! error TS2322: Type '"TypeTwo"' is not assignable to type '"TypeOne"'.
19+
declare let x2: { __typename?: 'TypeTwo' } & string;
20+
let y2: { __typename?: 'TypeOne' } & string = x2; // should error here
21+
~~
22+
!!! error TS2322: Type '{ __typename?: "TypeTwo"; } & string' is not assignable to type '{ __typename?: "TypeOne"; } & string'.
23+
!!! error TS2322: Type '{ __typename?: "TypeTwo"; } & string' is not assignable to type '{ __typename?: "TypeOne"; }'.
24+
!!! error TS2322: Types of property '__typename' are incompatible.
25+
!!! error TS2322: Type '"TypeTwo"' is not assignable to type '"TypeOne"'.
26+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//// [commonTypeIntersection.ts]
2+
declare let x1: { __typename?: 'TypeTwo' } & { a: boolean };
3+
let y1: { __typename?: 'TypeOne' } & { a: boolean} = x1; // should error here
4+
declare let x2: { __typename?: 'TypeTwo' } & string;
5+
let y2: { __typename?: 'TypeOne' } & string = x2; // should error here
6+
7+
8+
//// [commonTypeIntersection.js]
9+
var y1 = x1; // should error here
10+
var y2 = x2; // should error here
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
=== tests/cases/conformance/types/intersection/commonTypeIntersection.ts ===
2+
declare let x1: { __typename?: 'TypeTwo' } & { a: boolean };
3+
>x1 : Symbol(x1, Decl(commonTypeIntersection.ts, 0, 11))
4+
>__typename : Symbol(__typename, Decl(commonTypeIntersection.ts, 0, 17))
5+
>a : Symbol(a, Decl(commonTypeIntersection.ts, 0, 46))
6+
7+
let y1: { __typename?: 'TypeOne' } & { a: boolean} = x1; // should error here
8+
>y1 : Symbol(y1, Decl(commonTypeIntersection.ts, 1, 3))
9+
>__typename : Symbol(__typename, Decl(commonTypeIntersection.ts, 1, 9))
10+
>a : Symbol(a, Decl(commonTypeIntersection.ts, 1, 38))
11+
>x1 : Symbol(x1, Decl(commonTypeIntersection.ts, 0, 11))
12+
13+
declare let x2: { __typename?: 'TypeTwo' } & string;
14+
>x2 : Symbol(x2, Decl(commonTypeIntersection.ts, 2, 11))
15+
>__typename : Symbol(__typename, Decl(commonTypeIntersection.ts, 2, 17))
16+
17+
let y2: { __typename?: 'TypeOne' } & string = x2; // should error here
18+
>y2 : Symbol(y2, Decl(commonTypeIntersection.ts, 3, 3))
19+
>__typename : Symbol(__typename, Decl(commonTypeIntersection.ts, 3, 9))
20+
>x2 : Symbol(x2, Decl(commonTypeIntersection.ts, 2, 11))
21+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
=== tests/cases/conformance/types/intersection/commonTypeIntersection.ts ===
2+
declare let x1: { __typename?: 'TypeTwo' } & { a: boolean };
3+
>x1 : { __typename?: "TypeTwo"; } & { a: boolean; }
4+
>__typename : "TypeTwo"
5+
>a : boolean
6+
7+
let y1: { __typename?: 'TypeOne' } & { a: boolean} = x1; // should error here
8+
>y1 : { __typename?: "TypeOne"; } & { a: boolean; }
9+
>__typename : "TypeOne"
10+
>a : boolean
11+
>x1 : { __typename?: "TypeTwo"; } & { a: boolean; }
12+
13+
declare let x2: { __typename?: 'TypeTwo' } & string;
14+
>x2 : { __typename?: "TypeTwo"; } & string
15+
>__typename : "TypeTwo"
16+
17+
let y2: { __typename?: 'TypeOne' } & string = x2; // should error here
18+
>y2 : { __typename?: "TypeOne"; } & string
19+
>__typename : "TypeOne"
20+
>x2 : { __typename?: "TypeTwo"; } & string
21+

tests/baselines/reference/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.errors.txt

+34-28
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,23 @@ tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.t
55
Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
66
Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
77
Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
8-
Type 'T' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
9-
Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
10-
Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
11-
Type '"text"' is not assignable to type 'T & "text"'.
12-
Type '"text"' is not assignable to type 'T'.
13-
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
14-
Type 'T' is not assignable to type 'T & "text"'.
15-
Type '"text" | "email"' is not assignable to type 'T & "text"'.
16-
Type '"text"' is not assignable to type 'T & "text"'.
17-
Type '"text"' is not assignable to type 'T'.
18-
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
19-
Type 'T' is not assignable to type '"text"'.
20-
Type '"text" | "email"' is not assignable to type '"text"'.
21-
Type '"email"' is not assignable to type '"text"'.
8+
Type '"text"' is not assignable to type 'T & "text"'.
9+
Type '"text"' is not assignable to type 'T'.
10+
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
11+
Type 'T' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
12+
Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
13+
Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
14+
Type '"text"' is not assignable to type 'T & "text"'.
15+
Type '"text"' is not assignable to type 'T'.
16+
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
17+
Type 'T' is not assignable to type 'T & "text"'.
18+
Type '"text" | "email"' is not assignable to type 'T & "text"'.
19+
Type '"text"' is not assignable to type 'T & "text"'.
20+
Type '"text"' is not assignable to type 'T'.
21+
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
22+
Type 'T' is not assignable to type '"text"'.
23+
Type '"text" | "email"' is not assignable to type '"text"'.
24+
Type '"email"' is not assignable to type '"text"'.
2225

2326

2427
==== tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts (1 errors) ====
@@ -63,20 +66,23 @@ tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.t
6366
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
6467
!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
6568
!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
66-
!!! error TS2322: Type 'T' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
67-
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
68-
!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
69-
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
70-
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
71-
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
72-
!!! error TS2322: Type 'T' is not assignable to type 'T & "text"'.
73-
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'T & "text"'.
74-
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
75-
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
76-
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
77-
!!! error TS2322: Type 'T' is not assignable to type '"text"'.
78-
!!! error TS2322: Type '"text" | "email"' is not assignable to type '"text"'.
79-
!!! error TS2322: Type '"email"' is not assignable to type '"text"'.
69+
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
70+
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
71+
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
72+
!!! error TS2322: Type 'T' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
73+
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
74+
!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
75+
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
76+
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
77+
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
78+
!!! error TS2322: Type 'T' is not assignable to type 'T & "text"'.
79+
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'T & "text"'.
80+
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
81+
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
82+
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
83+
!!! error TS2322: Type 'T' is not assignable to type '"text"'.
84+
!!! error TS2322: Type '"text" | "email"' is not assignable to type '"text"'.
85+
!!! error TS2322: Type '"email"' is not assignable to type '"text"'.
8086
}
8187

8288
const newTextChannel = makeNewChannel('text');

0 commit comments

Comments
 (0)