Skip to content

Commit f2c487e

Browse files
authored
Merge pull request #14562 from Microsoft/fixLookupTypeRelation
Fix indexed access type relations
2 parents 456c397 + 4b4211f commit f2c487e

File tree

6 files changed

+419
-32
lines changed

6 files changed

+419
-32
lines changed

src/compiler/checker.ts

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3973,7 +3973,7 @@ namespace ts {
39733973
return true;
39743974
}
39753975
if (type.flags & TypeFlags.TypeVariable) {
3976-
const constraint = getBaseConstraintOfType(<TypeVariable>type);
3976+
const constraint = getBaseConstraintOfType(type);
39773977
return constraint && isValidBaseType(constraint) && isMixinConstructorType(constraint);
39783978
}
39793979
return false;
@@ -4989,16 +4989,32 @@ namespace ts {
49894989
}
49904990

49914991
function getConstraintOfType(type: TypeVariable | UnionOrIntersectionType): Type {
4992-
return type.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(<TypeParameter>type) : getBaseConstraintOfType(type);
4992+
return type.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(<TypeParameter>type) :
4993+
type.flags & TypeFlags.IndexedAccess ? getConstraintOfIndexedAccess(<IndexedAccessType>type) :
4994+
getBaseConstraintOfType(type);
49934995
}
49944996

49954997
function getConstraintOfTypeParameter(typeParameter: TypeParameter): Type {
49964998
return hasNonCircularBaseConstraint(typeParameter) ? getConstraintFromTypeParameter(typeParameter) : undefined;
49974999
}
49985000

4999-
function getBaseConstraintOfType(type: TypeVariable | UnionOrIntersectionType): Type {
5000-
const constraint = getResolvedBaseConstraint(type);
5001-
return constraint !== noConstraintType && constraint !== circularConstraintType ? constraint : undefined;
5001+
function getConstraintOfIndexedAccess(type: IndexedAccessType) {
5002+
const baseObjectType = getBaseConstraintOfType(type.objectType);
5003+
const baseIndexType = getBaseConstraintOfType(type.indexType);
5004+
return baseObjectType || baseIndexType ? getIndexedAccessType(baseObjectType || type.objectType, baseIndexType || type.indexType) : undefined;
5005+
}
5006+
5007+
function getBaseConstraintOfType(type: Type): Type {
5008+
if (type.flags & (TypeFlags.TypeVariable | TypeFlags.UnionOrIntersection)) {
5009+
const constraint = getResolvedBaseConstraint(<TypeVariable | UnionOrIntersectionType>type);
5010+
if (constraint !== noConstraintType && constraint !== circularConstraintType) {
5011+
return constraint;
5012+
}
5013+
}
5014+
else if (type.flags & TypeFlags.Index) {
5015+
return stringType;
5016+
}
5017+
return undefined;
50025018
}
50035019

50045020
function hasNonCircularBaseConstraint(type: TypeVariable): boolean {
@@ -5096,7 +5112,7 @@ namespace ts {
50965112
* type itself. Note that the apparent type of a union type is the union type itself.
50975113
*/
50985114
function getApparentType(type: Type): Type {
5099-
const t = type.flags & TypeFlags.TypeVariable ? getBaseConstraintOfType(<TypeVariable>type) || emptyObjectType : type;
5115+
const t = type.flags & TypeFlags.TypeVariable ? getBaseConstraintOfType(type) || emptyObjectType : type;
51005116
return t.flags & TypeFlags.Intersection ? getApparentTypeOfIntersectionType(<IntersectionType>t) :
51015117
t.flags & TypeFlags.StringLike ? globalStringType :
51025118
t.flags & TypeFlags.NumberLike ? globalNumberType :
@@ -7921,7 +7937,7 @@ namespace ts {
79217937
}
79227938
// A type S is related to a type T[K] if S is related to A[K], where K is string-like and
79237939
// A is the apparent type of S.
7924-
const constraint = getBaseConstraintOfType(<IndexedAccessType>target);
7940+
const constraint = getBaseConstraintOfType(target);
79257941
if (constraint) {
79267942
if (result = isRelatedTo(source, constraint, reportErrors)) {
79277943
errorInfo = saveErrorInfo;
@@ -7961,7 +7977,7 @@ namespace ts {
79617977
else if (source.flags & TypeFlags.IndexedAccess) {
79627978
// A type S[K] is related to a type T if A[K] is related to T, where K is string-like and
79637979
// A is the apparent type of S.
7964-
const constraint = getBaseConstraintOfType(<IndexedAccessType>source);
7980+
const constraint = getConstraintOfType(<IndexedAccessType>source);
79657981
if (constraint) {
79667982
if (result = isRelatedTo(constraint, target, reportErrors)) {
79677983
errorInfo = saveErrorInfo;
@@ -9874,7 +9890,7 @@ namespace ts {
98749890
return strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts;
98759891
}
98769892
if (flags & TypeFlags.TypeVariable) {
9877-
return getTypeFacts(getBaseConstraintOfType(<TypeVariable>type) || emptyObjectType);
9893+
return getTypeFacts(getBaseConstraintOfType(type) || emptyObjectType);
98789894
}
98799895
if (flags & TypeFlags.UnionOrIntersection) {
98809896
return getTypeFactsOfTypes((<UnionOrIntersectionType>type).types);
@@ -10686,7 +10702,7 @@ namespace ts {
1068610702
return targetType;
1068710703
}
1068810704
if (type.flags & TypeFlags.TypeVariable) {
10689-
const constraint = getBaseConstraintOfType(<TypeVariable>type) || anyType;
10705+
const constraint = getBaseConstraintOfType(type) || anyType;
1069010706
if (isTypeSubtypeOf(targetType, constraint)) {
1069110707
return getIntersectionType([type, targetType]);
1069210708
}
@@ -16234,7 +16250,7 @@ namespace ts {
1623416250
function isLiteralContextualType(contextualType: Type) {
1623516251
if (contextualType) {
1623616252
if (contextualType.flags & TypeFlags.TypeVariable) {
16237-
const constraint = getBaseConstraintOfType(<TypeVariable>contextualType) || emptyObjectType;
16253+
const constraint = getBaseConstraintOfType(contextualType) || emptyObjectType;
1623816254
// If the type parameter is constrained to the base primitive type we're checking for,
1623916255
// consider this a literal context. For example, given a type parameter 'T extends string',
1624016256
// this causes us to infer string literal types for T.
@@ -17124,7 +17140,7 @@ namespace ts {
1712417140
// Check if we're indexing with a numeric type and the object type is a generic
1712517141
// type with a constraint that has a numeric index signature.
1712617142
if (maybeTypeOfKind(objectType, TypeFlags.TypeVariable) && isTypeOfKind(indexType, TypeFlags.NumberLike)) {
17127-
const constraint = getBaseConstraintOfType(<TypeVariable | UnionOrIntersectionType>objectType);
17143+
const constraint = getBaseConstraintOfType(objectType);
1712817144
if (constraint && getIndexInfoOfType(constraint, IndexKind.Number)) {
1712917145
return type;
1713017146
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//// [indexedAccessTypeConstraints.ts]
2+
3+
// Repro from #14557
4+
5+
interface IData<T> {
6+
content: T;
7+
}
8+
9+
type Data<T> = {
10+
get: <K extends keyof T>(prop: K) => T[K];
11+
};
12+
13+
class Parent<M> {
14+
private data: Data<M>;
15+
getData(): Data<M> {
16+
return this.data;
17+
}
18+
}
19+
20+
export class Foo<C> extends Parent<IData<C>> {
21+
getContent(): C {
22+
return this.getData().get('content');
23+
}
24+
}
25+
26+
export class Bar<C, T extends IData<C>> extends Parent<T> {
27+
getContent(): C {
28+
return this.getData().get('content');
29+
}
30+
}
31+
32+
// Repro from #14557
33+
34+
function foo<C, T extends { content: C }>(x: C, y: T['content']) {
35+
x = y;
36+
}
37+
38+
39+
//// [indexedAccessTypeConstraints.js]
40+
// Repro from #14557
41+
"use strict";
42+
var __extends = (this && this.__extends) || (function () {
43+
var extendStatics = Object.setPrototypeOf ||
44+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
45+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
46+
return function (d, b) {
47+
extendStatics(d, b);
48+
function __() { this.constructor = d; }
49+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
50+
};
51+
})();
52+
exports.__esModule = true;
53+
var Parent = (function () {
54+
function Parent() {
55+
}
56+
Parent.prototype.getData = function () {
57+
return this.data;
58+
};
59+
return Parent;
60+
}());
61+
var Foo = (function (_super) {
62+
__extends(Foo, _super);
63+
function Foo() {
64+
return _super !== null && _super.apply(this, arguments) || this;
65+
}
66+
Foo.prototype.getContent = function () {
67+
return this.getData().get('content');
68+
};
69+
return Foo;
70+
}(Parent));
71+
exports.Foo = Foo;
72+
var Bar = (function (_super) {
73+
__extends(Bar, _super);
74+
function Bar() {
75+
return _super !== null && _super.apply(this, arguments) || this;
76+
}
77+
Bar.prototype.getContent = function () {
78+
return this.getData().get('content');
79+
};
80+
return Bar;
81+
}(Parent));
82+
exports.Bar = Bar;
83+
// Repro from #14557
84+
function foo(x, y) {
85+
x = y;
86+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
=== tests/cases/compiler/indexedAccessTypeConstraints.ts ===
2+
3+
// Repro from #14557
4+
5+
interface IData<T> {
6+
>IData : Symbol(IData, Decl(indexedAccessTypeConstraints.ts, 0, 0))
7+
>T : Symbol(T, Decl(indexedAccessTypeConstraints.ts, 3, 16))
8+
9+
content: T;
10+
>content : Symbol(IData.content, Decl(indexedAccessTypeConstraints.ts, 3, 20))
11+
>T : Symbol(T, Decl(indexedAccessTypeConstraints.ts, 3, 16))
12+
}
13+
14+
type Data<T> = {
15+
>Data : Symbol(Data, Decl(indexedAccessTypeConstraints.ts, 5, 1))
16+
>T : Symbol(T, Decl(indexedAccessTypeConstraints.ts, 7, 10))
17+
18+
get: <K extends keyof T>(prop: K) => T[K];
19+
>get : Symbol(get, Decl(indexedAccessTypeConstraints.ts, 7, 16))
20+
>K : Symbol(K, Decl(indexedAccessTypeConstraints.ts, 8, 10))
21+
>T : Symbol(T, Decl(indexedAccessTypeConstraints.ts, 7, 10))
22+
>prop : Symbol(prop, Decl(indexedAccessTypeConstraints.ts, 8, 29))
23+
>K : Symbol(K, Decl(indexedAccessTypeConstraints.ts, 8, 10))
24+
>T : Symbol(T, Decl(indexedAccessTypeConstraints.ts, 7, 10))
25+
>K : Symbol(K, Decl(indexedAccessTypeConstraints.ts, 8, 10))
26+
27+
};
28+
29+
class Parent<M> {
30+
>Parent : Symbol(Parent, Decl(indexedAccessTypeConstraints.ts, 9, 2))
31+
>M : Symbol(M, Decl(indexedAccessTypeConstraints.ts, 11, 13))
32+
33+
private data: Data<M>;
34+
>data : Symbol(Parent.data, Decl(indexedAccessTypeConstraints.ts, 11, 17))
35+
>Data : Symbol(Data, Decl(indexedAccessTypeConstraints.ts, 5, 1))
36+
>M : Symbol(M, Decl(indexedAccessTypeConstraints.ts, 11, 13))
37+
38+
getData(): Data<M> {
39+
>getData : Symbol(Parent.getData, Decl(indexedAccessTypeConstraints.ts, 12, 26))
40+
>Data : Symbol(Data, Decl(indexedAccessTypeConstraints.ts, 5, 1))
41+
>M : Symbol(M, Decl(indexedAccessTypeConstraints.ts, 11, 13))
42+
43+
return this.data;
44+
>this.data : Symbol(Parent.data, Decl(indexedAccessTypeConstraints.ts, 11, 17))
45+
>this : Symbol(Parent, Decl(indexedAccessTypeConstraints.ts, 9, 2))
46+
>data : Symbol(Parent.data, Decl(indexedAccessTypeConstraints.ts, 11, 17))
47+
}
48+
}
49+
50+
export class Foo<C> extends Parent<IData<C>> {
51+
>Foo : Symbol(Foo, Decl(indexedAccessTypeConstraints.ts, 16, 1))
52+
>C : Symbol(C, Decl(indexedAccessTypeConstraints.ts, 18, 17))
53+
>Parent : Symbol(Parent, Decl(indexedAccessTypeConstraints.ts, 9, 2))
54+
>IData : Symbol(IData, Decl(indexedAccessTypeConstraints.ts, 0, 0))
55+
>C : Symbol(C, Decl(indexedAccessTypeConstraints.ts, 18, 17))
56+
57+
getContent(): C {
58+
>getContent : Symbol(Foo.getContent, Decl(indexedAccessTypeConstraints.ts, 18, 46))
59+
>C : Symbol(C, Decl(indexedAccessTypeConstraints.ts, 18, 17))
60+
61+
return this.getData().get('content');
62+
>this.getData().get : Symbol(get, Decl(indexedAccessTypeConstraints.ts, 7, 16))
63+
>this.getData : Symbol(Parent.getData, Decl(indexedAccessTypeConstraints.ts, 12, 26))
64+
>this : Symbol(Foo, Decl(indexedAccessTypeConstraints.ts, 16, 1))
65+
>getData : Symbol(Parent.getData, Decl(indexedAccessTypeConstraints.ts, 12, 26))
66+
>get : Symbol(get, Decl(indexedAccessTypeConstraints.ts, 7, 16))
67+
}
68+
}
69+
70+
export class Bar<C, T extends IData<C>> extends Parent<T> {
71+
>Bar : Symbol(Bar, Decl(indexedAccessTypeConstraints.ts, 22, 1))
72+
>C : Symbol(C, Decl(indexedAccessTypeConstraints.ts, 24, 17))
73+
>T : Symbol(T, Decl(indexedAccessTypeConstraints.ts, 24, 19))
74+
>IData : Symbol(IData, Decl(indexedAccessTypeConstraints.ts, 0, 0))
75+
>C : Symbol(C, Decl(indexedAccessTypeConstraints.ts, 24, 17))
76+
>Parent : Symbol(Parent, Decl(indexedAccessTypeConstraints.ts, 9, 2))
77+
>T : Symbol(T, Decl(indexedAccessTypeConstraints.ts, 24, 19))
78+
79+
getContent(): C {
80+
>getContent : Symbol(Bar.getContent, Decl(indexedAccessTypeConstraints.ts, 24, 59))
81+
>C : Symbol(C, Decl(indexedAccessTypeConstraints.ts, 24, 17))
82+
83+
return this.getData().get('content');
84+
>this.getData().get : Symbol(get, Decl(indexedAccessTypeConstraints.ts, 7, 16))
85+
>this.getData : Symbol(Parent.getData, Decl(indexedAccessTypeConstraints.ts, 12, 26))
86+
>this : Symbol(Bar, Decl(indexedAccessTypeConstraints.ts, 22, 1))
87+
>getData : Symbol(Parent.getData, Decl(indexedAccessTypeConstraints.ts, 12, 26))
88+
>get : Symbol(get, Decl(indexedAccessTypeConstraints.ts, 7, 16))
89+
}
90+
}
91+
92+
// Repro from #14557
93+
94+
function foo<C, T extends { content: C }>(x: C, y: T['content']) {
95+
>foo : Symbol(foo, Decl(indexedAccessTypeConstraints.ts, 28, 1))
96+
>C : Symbol(C, Decl(indexedAccessTypeConstraints.ts, 32, 13))
97+
>T : Symbol(T, Decl(indexedAccessTypeConstraints.ts, 32, 15))
98+
>content : Symbol(content, Decl(indexedAccessTypeConstraints.ts, 32, 27))
99+
>C : Symbol(C, Decl(indexedAccessTypeConstraints.ts, 32, 13))
100+
>x : Symbol(x, Decl(indexedAccessTypeConstraints.ts, 32, 42))
101+
>C : Symbol(C, Decl(indexedAccessTypeConstraints.ts, 32, 13))
102+
>y : Symbol(y, Decl(indexedAccessTypeConstraints.ts, 32, 47))
103+
>T : Symbol(T, Decl(indexedAccessTypeConstraints.ts, 32, 15))
104+
105+
x = y;
106+
>x : Symbol(x, Decl(indexedAccessTypeConstraints.ts, 32, 42))
107+
>y : Symbol(y, Decl(indexedAccessTypeConstraints.ts, 32, 47))
108+
}
109+

0 commit comments

Comments
 (0)