Skip to content

Commit 02fe840

Browse files
authored
Get constraint with this argument of the type parameter for comparisons (microsoft#21210)
* Get constraint with this argument of the type parameter for comparisons * Also instantiate indexed accesses * Add much simpler test
1 parent 4c22bf7 commit 02fe840

10 files changed

+455
-5
lines changed

src/compiler/checker.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10954,9 +10954,12 @@ namespace ts {
1095410954
return result;
1095510955
}
1095610956
}
10957-
else if (result = isRelatedTo(constraint, target, reportErrors)) {
10958-
errorInfo = saveErrorInfo;
10959-
return result;
10957+
else {
10958+
const instantiated = getTypeWithThisArgument(constraint, source);
10959+
if (result = isRelatedTo(instantiated, target, reportErrors)) {
10960+
errorInfo = saveErrorInfo;
10961+
return result;
10962+
}
1096010963
}
1096110964
}
1096210965
else if (source.flags & TypeFlags.Index) {
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//// [collectionPatternNoError.ts]
2+
interface MsgConstructor<T extends Message> {
3+
new(data: Array<{}>): T;
4+
}
5+
class Message {
6+
clone(): this {
7+
return this;
8+
}
9+
}
10+
interface MessageList<T extends Message> extends Message {
11+
methodOnMessageList(): T[];
12+
}
13+
14+
function fetchMsg<V extends Message>(protoCtor: MsgConstructor<V>): V {
15+
return null!;
16+
}
17+
18+
class DataProvider<T extends Message, U extends MessageList<T>> {
19+
constructor(
20+
private readonly message: MsgConstructor<T>,
21+
private readonly messageList: MsgConstructor<U>,
22+
) { }
23+
24+
fetch() {
25+
const messageList = fetchMsg(this.messageList);
26+
messageList.methodOnMessageList();
27+
}
28+
}
29+
30+
// The same bug as the above but using indexed accesses
31+
// (won't surface directly unless unsound indexed access assignments are forbidden)
32+
function f<
33+
U extends {TType: MessageList<T>},
34+
T extends Message
35+
>(message: MsgConstructor<T>, messageList: MsgConstructor<U["TType"]>) {
36+
fetchMsg(messageList).methodOnMessageList();
37+
}
38+
39+
40+
//// [collectionPatternNoError.js]
41+
var Message = /** @class */ (function () {
42+
function Message() {
43+
}
44+
Message.prototype.clone = function () {
45+
return this;
46+
};
47+
return Message;
48+
}());
49+
function fetchMsg(protoCtor) {
50+
return null;
51+
}
52+
var DataProvider = /** @class */ (function () {
53+
function DataProvider(message, messageList) {
54+
this.message = message;
55+
this.messageList = messageList;
56+
}
57+
DataProvider.prototype.fetch = function () {
58+
var messageList = fetchMsg(this.messageList);
59+
messageList.methodOnMessageList();
60+
};
61+
return DataProvider;
62+
}());
63+
// The same bug as the above but using indexed accesses
64+
// (won't surface directly unless unsound indexed access assignments are forbidden)
65+
function f(message, messageList) {
66+
fetchMsg(messageList).methodOnMessageList();
67+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
=== tests/cases/compiler/collectionPatternNoError.ts ===
2+
interface MsgConstructor<T extends Message> {
3+
>MsgConstructor : Symbol(MsgConstructor, Decl(collectionPatternNoError.ts, 0, 0))
4+
>T : Symbol(T, Decl(collectionPatternNoError.ts, 0, 25))
5+
>Message : Symbol(Message, Decl(collectionPatternNoError.ts, 2, 1))
6+
7+
new(data: Array<{}>): T;
8+
>data : Symbol(data, Decl(collectionPatternNoError.ts, 1, 6))
9+
>Array : Symbol(Array, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
10+
>T : Symbol(T, Decl(collectionPatternNoError.ts, 0, 25))
11+
}
12+
class Message {
13+
>Message : Symbol(Message, Decl(collectionPatternNoError.ts, 2, 1))
14+
15+
clone(): this {
16+
>clone : Symbol(Message.clone, Decl(collectionPatternNoError.ts, 3, 15))
17+
18+
return this;
19+
>this : Symbol(Message, Decl(collectionPatternNoError.ts, 2, 1))
20+
}
21+
}
22+
interface MessageList<T extends Message> extends Message {
23+
>MessageList : Symbol(MessageList, Decl(collectionPatternNoError.ts, 7, 1))
24+
>T : Symbol(T, Decl(collectionPatternNoError.ts, 8, 22))
25+
>Message : Symbol(Message, Decl(collectionPatternNoError.ts, 2, 1))
26+
>Message : Symbol(Message, Decl(collectionPatternNoError.ts, 2, 1))
27+
28+
methodOnMessageList(): T[];
29+
>methodOnMessageList : Symbol(MessageList.methodOnMessageList, Decl(collectionPatternNoError.ts, 8, 58))
30+
>T : Symbol(T, Decl(collectionPatternNoError.ts, 8, 22))
31+
}
32+
33+
function fetchMsg<V extends Message>(protoCtor: MsgConstructor<V>): V {
34+
>fetchMsg : Symbol(fetchMsg, Decl(collectionPatternNoError.ts, 10, 1))
35+
>V : Symbol(V, Decl(collectionPatternNoError.ts, 12, 18))
36+
>Message : Symbol(Message, Decl(collectionPatternNoError.ts, 2, 1))
37+
>protoCtor : Symbol(protoCtor, Decl(collectionPatternNoError.ts, 12, 37))
38+
>MsgConstructor : Symbol(MsgConstructor, Decl(collectionPatternNoError.ts, 0, 0))
39+
>V : Symbol(V, Decl(collectionPatternNoError.ts, 12, 18))
40+
>V : Symbol(V, Decl(collectionPatternNoError.ts, 12, 18))
41+
42+
return null!;
43+
}
44+
45+
class DataProvider<T extends Message, U extends MessageList<T>> {
46+
>DataProvider : Symbol(DataProvider, Decl(collectionPatternNoError.ts, 14, 1))
47+
>T : Symbol(T, Decl(collectionPatternNoError.ts, 16, 19))
48+
>Message : Symbol(Message, Decl(collectionPatternNoError.ts, 2, 1))
49+
>U : Symbol(U, Decl(collectionPatternNoError.ts, 16, 37))
50+
>MessageList : Symbol(MessageList, Decl(collectionPatternNoError.ts, 7, 1))
51+
>T : Symbol(T, Decl(collectionPatternNoError.ts, 16, 19))
52+
53+
constructor(
54+
private readonly message: MsgConstructor<T>,
55+
>message : Symbol(DataProvider.message, Decl(collectionPatternNoError.ts, 17, 14))
56+
>MsgConstructor : Symbol(MsgConstructor, Decl(collectionPatternNoError.ts, 0, 0))
57+
>T : Symbol(T, Decl(collectionPatternNoError.ts, 16, 19))
58+
59+
private readonly messageList: MsgConstructor<U>,
60+
>messageList : Symbol(DataProvider.messageList, Decl(collectionPatternNoError.ts, 18, 48))
61+
>MsgConstructor : Symbol(MsgConstructor, Decl(collectionPatternNoError.ts, 0, 0))
62+
>U : Symbol(U, Decl(collectionPatternNoError.ts, 16, 37))
63+
64+
) { }
65+
66+
fetch() {
67+
>fetch : Symbol(DataProvider.fetch, Decl(collectionPatternNoError.ts, 20, 7))
68+
69+
const messageList = fetchMsg(this.messageList);
70+
>messageList : Symbol(messageList, Decl(collectionPatternNoError.ts, 23, 9))
71+
>fetchMsg : Symbol(fetchMsg, Decl(collectionPatternNoError.ts, 10, 1))
72+
>this.messageList : Symbol(DataProvider.messageList, Decl(collectionPatternNoError.ts, 18, 48))
73+
>this : Symbol(DataProvider, Decl(collectionPatternNoError.ts, 14, 1))
74+
>messageList : Symbol(DataProvider.messageList, Decl(collectionPatternNoError.ts, 18, 48))
75+
76+
messageList.methodOnMessageList();
77+
>messageList.methodOnMessageList : Symbol(MessageList.methodOnMessageList, Decl(collectionPatternNoError.ts, 8, 58))
78+
>messageList : Symbol(messageList, Decl(collectionPatternNoError.ts, 23, 9))
79+
>methodOnMessageList : Symbol(MessageList.methodOnMessageList, Decl(collectionPatternNoError.ts, 8, 58))
80+
}
81+
}
82+
83+
// The same bug as the above but using indexed accesses
84+
// (won't surface directly unless unsound indexed access assignments are forbidden)
85+
function f<
86+
>f : Symbol(f, Decl(collectionPatternNoError.ts, 26, 1))
87+
88+
U extends {TType: MessageList<T>},
89+
>U : Symbol(U, Decl(collectionPatternNoError.ts, 30, 11))
90+
>TType : Symbol(TType, Decl(collectionPatternNoError.ts, 31, 13))
91+
>MessageList : Symbol(MessageList, Decl(collectionPatternNoError.ts, 7, 1))
92+
>T : Symbol(T, Decl(collectionPatternNoError.ts, 31, 36))
93+
94+
T extends Message
95+
>T : Symbol(T, Decl(collectionPatternNoError.ts, 31, 36))
96+
>Message : Symbol(Message, Decl(collectionPatternNoError.ts, 2, 1))
97+
98+
>(message: MsgConstructor<T>, messageList: MsgConstructor<U["TType"]>) {
99+
>message : Symbol(message, Decl(collectionPatternNoError.ts, 33, 2))
100+
>MsgConstructor : Symbol(MsgConstructor, Decl(collectionPatternNoError.ts, 0, 0))
101+
>T : Symbol(T, Decl(collectionPatternNoError.ts, 31, 36))
102+
>messageList : Symbol(messageList, Decl(collectionPatternNoError.ts, 33, 29))
103+
>MsgConstructor : Symbol(MsgConstructor, Decl(collectionPatternNoError.ts, 0, 0))
104+
>U : Symbol(U, Decl(collectionPatternNoError.ts, 30, 11))
105+
106+
fetchMsg(messageList).methodOnMessageList();
107+
>fetchMsg(messageList).methodOnMessageList : Symbol(MessageList.methodOnMessageList, Decl(collectionPatternNoError.ts, 8, 58))
108+
>fetchMsg : Symbol(fetchMsg, Decl(collectionPatternNoError.ts, 10, 1))
109+
>messageList : Symbol(messageList, Decl(collectionPatternNoError.ts, 33, 29))
110+
>methodOnMessageList : Symbol(MessageList.methodOnMessageList, Decl(collectionPatternNoError.ts, 8, 58))
111+
}
112+
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
=== tests/cases/compiler/collectionPatternNoError.ts ===
2+
interface MsgConstructor<T extends Message> {
3+
>MsgConstructor : MsgConstructor<T>
4+
>T : T
5+
>Message : Message
6+
7+
new(data: Array<{}>): T;
8+
>data : {}[]
9+
>Array : T[]
10+
>T : T
11+
}
12+
class Message {
13+
>Message : Message
14+
15+
clone(): this {
16+
>clone : () => this
17+
18+
return this;
19+
>this : this
20+
}
21+
}
22+
interface MessageList<T extends Message> extends Message {
23+
>MessageList : MessageList<T>
24+
>T : T
25+
>Message : Message
26+
>Message : Message
27+
28+
methodOnMessageList(): T[];
29+
>methodOnMessageList : () => T[]
30+
>T : T
31+
}
32+
33+
function fetchMsg<V extends Message>(protoCtor: MsgConstructor<V>): V {
34+
>fetchMsg : <V extends Message>(protoCtor: MsgConstructor<V>) => V
35+
>V : V
36+
>Message : Message
37+
>protoCtor : MsgConstructor<V>
38+
>MsgConstructor : MsgConstructor<T>
39+
>V : V
40+
>V : V
41+
42+
return null!;
43+
>null! : null
44+
>null : null
45+
}
46+
47+
class DataProvider<T extends Message, U extends MessageList<T>> {
48+
>DataProvider : DataProvider<T, U>
49+
>T : T
50+
>Message : Message
51+
>U : U
52+
>MessageList : MessageList<T>
53+
>T : T
54+
55+
constructor(
56+
private readonly message: MsgConstructor<T>,
57+
>message : MsgConstructor<T>
58+
>MsgConstructor : MsgConstructor<T>
59+
>T : T
60+
61+
private readonly messageList: MsgConstructor<U>,
62+
>messageList : MsgConstructor<U>
63+
>MsgConstructor : MsgConstructor<T>
64+
>U : U
65+
66+
) { }
67+
68+
fetch() {
69+
>fetch : () => void
70+
71+
const messageList = fetchMsg(this.messageList);
72+
>messageList : U
73+
>fetchMsg(this.messageList) : U
74+
>fetchMsg : <V extends Message>(protoCtor: MsgConstructor<V>) => V
75+
>this.messageList : MsgConstructor<U>
76+
>this : this
77+
>messageList : MsgConstructor<U>
78+
79+
messageList.methodOnMessageList();
80+
>messageList.methodOnMessageList() : T[]
81+
>messageList.methodOnMessageList : () => T[]
82+
>messageList : U
83+
>methodOnMessageList : () => T[]
84+
}
85+
}
86+
87+
// The same bug as the above but using indexed accesses
88+
// (won't surface directly unless unsound indexed access assignments are forbidden)
89+
function f<
90+
>f : <U extends { TType: MessageList<T>; }, T extends Message>(message: MsgConstructor<T>, messageList: MsgConstructor<U["TType"]>) => void
91+
92+
U extends {TType: MessageList<T>},
93+
>U : U
94+
>TType : MessageList<T>
95+
>MessageList : MessageList<T>
96+
>T : T
97+
98+
T extends Message
99+
>T : T
100+
>Message : Message
101+
102+
>(message: MsgConstructor<T>, messageList: MsgConstructor<U["TType"]>) {
103+
>message : MsgConstructor<T>
104+
>MsgConstructor : MsgConstructor<T>
105+
>T : T
106+
>messageList : MsgConstructor<U["TType"]>
107+
>MsgConstructor : MsgConstructor<T>
108+
>U : U
109+
110+
fetchMsg(messageList).methodOnMessageList();
111+
>fetchMsg(messageList).methodOnMessageList() : T[]
112+
>fetchMsg(messageList).methodOnMessageList : () => T[]
113+
>fetchMsg(messageList) : U["TType"]
114+
>fetchMsg : <V extends Message>(protoCtor: MsgConstructor<V>) => V
115+
>messageList : MsgConstructor<U["TType"]>
116+
>methodOnMessageList : () => T[]
117+
}
118+

tests/baselines/reference/fuzzy.errors.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ tests/cases/compiler/fuzzy.ts(21,13): error TS2322: Type '{ anything: number; on
44
Types of property 'oneI' are incompatible.
55
Type 'this' is not assignable to type 'I'.
66
Type 'C' is not assignable to type 'I'.
7-
Property 'alsoWorks' is missing in type 'C'.
87
tests/cases/compiler/fuzzy.ts(25,20): error TS2352: Type '{ oneI: this; }' cannot be converted to type 'R'.
98
Property 'anything' is missing in type '{ oneI: this; }'.
109

@@ -39,7 +38,6 @@ tests/cases/compiler/fuzzy.ts(25,20): error TS2352: Type '{ oneI: this; }' canno
3938
!!! error TS2322: Types of property 'oneI' are incompatible.
4039
!!! error TS2322: Type 'this' is not assignable to type 'I'.
4140
!!! error TS2322: Type 'C' is not assignable to type 'I'.
42-
!!! error TS2322: Property 'alsoWorks' is missing in type 'C'.
4341
}
4442

4543
worksToo():R {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//// [subclassWithPolymorphicThisIsAssignable.ts]
2+
/* taken from mongoose.Document */
3+
interface Document {
4+
increment(): this;
5+
}
6+
7+
/* our custom model extends the mongoose document */
8+
interface CustomDocument extends Document { }
9+
10+
export class Example<Z extends CustomDocument> {
11+
constructor() {
12+
// types of increment not compatible??
13+
this.test<Z>();
14+
}
15+
16+
public test<Z extends Document>() { }
17+
}
18+
19+
20+
//// [subclassWithPolymorphicThisIsAssignable.js]
21+
"use strict";
22+
exports.__esModule = true;
23+
var Example = /** @class */ (function () {
24+
function Example() {
25+
// types of increment not compatible??
26+
this.test();
27+
}
28+
Example.prototype.test = function () { };
29+
return Example;
30+
}());
31+
exports.Example = Example;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
=== tests/cases/compiler/subclassWithPolymorphicThisIsAssignable.ts ===
2+
/* taken from mongoose.Document */
3+
interface Document {
4+
>Document : Symbol(Document, Decl(subclassWithPolymorphicThisIsAssignable.ts, 0, 0))
5+
6+
increment(): this;
7+
>increment : Symbol(Document.increment, Decl(subclassWithPolymorphicThisIsAssignable.ts, 1, 20))
8+
}
9+
10+
/* our custom model extends the mongoose document */
11+
interface CustomDocument extends Document { }
12+
>CustomDocument : Symbol(CustomDocument, Decl(subclassWithPolymorphicThisIsAssignable.ts, 3, 1))
13+
>Document : Symbol(Document, Decl(subclassWithPolymorphicThisIsAssignable.ts, 0, 0))
14+
15+
export class Example<Z extends CustomDocument> {
16+
>Example : Symbol(Example, Decl(subclassWithPolymorphicThisIsAssignable.ts, 6, 45))
17+
>Z : Symbol(Z, Decl(subclassWithPolymorphicThisIsAssignable.ts, 8, 21))
18+
>CustomDocument : Symbol(CustomDocument, Decl(subclassWithPolymorphicThisIsAssignable.ts, 3, 1))
19+
20+
constructor() {
21+
// types of increment not compatible??
22+
this.test<Z>();
23+
>this.test : Symbol(Example.test, Decl(subclassWithPolymorphicThisIsAssignable.ts, 12, 5))
24+
>this : Symbol(Example, Decl(subclassWithPolymorphicThisIsAssignable.ts, 6, 45))
25+
>test : Symbol(Example.test, Decl(subclassWithPolymorphicThisIsAssignable.ts, 12, 5))
26+
>Z : Symbol(Z, Decl(subclassWithPolymorphicThisIsAssignable.ts, 8, 21))
27+
}
28+
29+
public test<Z extends Document>() { }
30+
>test : Symbol(Example.test, Decl(subclassWithPolymorphicThisIsAssignable.ts, 12, 5))
31+
>Z : Symbol(Z, Decl(subclassWithPolymorphicThisIsAssignable.ts, 14, 16))
32+
>Document : Symbol(Document, Decl(subclassWithPolymorphicThisIsAssignable.ts, 0, 0))
33+
}
34+

0 commit comments

Comments
 (0)