Skip to content

Get constraint with this argument of the type parameter for comparisons #21210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10960,9 +10960,12 @@ namespace ts {
return result;
}
}
else if (result = isRelatedTo(constraint, target, reportErrors)) {
errorInfo = saveErrorInfo;
return result;
else {
const instantiated = getTypeWithThisArgument(constraint, source);
if (result = isRelatedTo(instantiated, target, reportErrors)) {
errorInfo = saveErrorInfo;
return result;
}
}
}
else if (source.flags & TypeFlags.Index) {
Expand Down
67 changes: 67 additions & 0 deletions tests/baselines/reference/collectionPatternNoError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//// [collectionPatternNoError.ts]
interface MsgConstructor<T extends Message> {
new(data: Array<{}>): T;
}
class Message {
clone(): this {
return this;
}
}
interface MessageList<T extends Message> extends Message {
methodOnMessageList(): T[];
}

function fetchMsg<V extends Message>(protoCtor: MsgConstructor<V>): V {
return null!;
}

class DataProvider<T extends Message, U extends MessageList<T>> {
constructor(
private readonly message: MsgConstructor<T>,
private readonly messageList: MsgConstructor<U>,
) { }

fetch() {
const messageList = fetchMsg(this.messageList);
messageList.methodOnMessageList();
}
}

// The same bug as the above but using indexed accesses
// (won't surface directly unless unsound indexed access assignments are forbidden)
function f<
U extends {TType: MessageList<T>},
T extends Message
>(message: MsgConstructor<T>, messageList: MsgConstructor<U["TType"]>) {
fetchMsg(messageList).methodOnMessageList();
}


//// [collectionPatternNoError.js]
var Message = /** @class */ (function () {
function Message() {
}
Message.prototype.clone = function () {
return this;
};
return Message;
}());
function fetchMsg(protoCtor) {
return null;
}
var DataProvider = /** @class */ (function () {
function DataProvider(message, messageList) {
this.message = message;
this.messageList = messageList;
}
DataProvider.prototype.fetch = function () {
var messageList = fetchMsg(this.messageList);
messageList.methodOnMessageList();
};
return DataProvider;
}());
// The same bug as the above but using indexed accesses
// (won't surface directly unless unsound indexed access assignments are forbidden)
function f(message, messageList) {
fetchMsg(messageList).methodOnMessageList();
}
112 changes: 112 additions & 0 deletions tests/baselines/reference/collectionPatternNoError.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
=== tests/cases/compiler/collectionPatternNoError.ts ===
interface MsgConstructor<T extends Message> {
>MsgConstructor : Symbol(MsgConstructor, Decl(collectionPatternNoError.ts, 0, 0))
>T : Symbol(T, Decl(collectionPatternNoError.ts, 0, 25))
>Message : Symbol(Message, Decl(collectionPatternNoError.ts, 2, 1))

new(data: Array<{}>): T;
>data : Symbol(data, Decl(collectionPatternNoError.ts, 1, 6))
>Array : Symbol(Array, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>T : Symbol(T, Decl(collectionPatternNoError.ts, 0, 25))
}
class Message {
>Message : Symbol(Message, Decl(collectionPatternNoError.ts, 2, 1))

clone(): this {
>clone : Symbol(Message.clone, Decl(collectionPatternNoError.ts, 3, 15))

return this;
>this : Symbol(Message, Decl(collectionPatternNoError.ts, 2, 1))
}
}
interface MessageList<T extends Message> extends Message {
>MessageList : Symbol(MessageList, Decl(collectionPatternNoError.ts, 7, 1))
>T : Symbol(T, Decl(collectionPatternNoError.ts, 8, 22))
>Message : Symbol(Message, Decl(collectionPatternNoError.ts, 2, 1))
>Message : Symbol(Message, Decl(collectionPatternNoError.ts, 2, 1))

methodOnMessageList(): T[];
>methodOnMessageList : Symbol(MessageList.methodOnMessageList, Decl(collectionPatternNoError.ts, 8, 58))
>T : Symbol(T, Decl(collectionPatternNoError.ts, 8, 22))
}

function fetchMsg<V extends Message>(protoCtor: MsgConstructor<V>): V {
>fetchMsg : Symbol(fetchMsg, Decl(collectionPatternNoError.ts, 10, 1))
>V : Symbol(V, Decl(collectionPatternNoError.ts, 12, 18))
>Message : Symbol(Message, Decl(collectionPatternNoError.ts, 2, 1))
>protoCtor : Symbol(protoCtor, Decl(collectionPatternNoError.ts, 12, 37))
>MsgConstructor : Symbol(MsgConstructor, Decl(collectionPatternNoError.ts, 0, 0))
>V : Symbol(V, Decl(collectionPatternNoError.ts, 12, 18))
>V : Symbol(V, Decl(collectionPatternNoError.ts, 12, 18))

return null!;
}

class DataProvider<T extends Message, U extends MessageList<T>> {
>DataProvider : Symbol(DataProvider, Decl(collectionPatternNoError.ts, 14, 1))
>T : Symbol(T, Decl(collectionPatternNoError.ts, 16, 19))
>Message : Symbol(Message, Decl(collectionPatternNoError.ts, 2, 1))
>U : Symbol(U, Decl(collectionPatternNoError.ts, 16, 37))
>MessageList : Symbol(MessageList, Decl(collectionPatternNoError.ts, 7, 1))
>T : Symbol(T, Decl(collectionPatternNoError.ts, 16, 19))

constructor(
private readonly message: MsgConstructor<T>,
>message : Symbol(DataProvider.message, Decl(collectionPatternNoError.ts, 17, 14))
>MsgConstructor : Symbol(MsgConstructor, Decl(collectionPatternNoError.ts, 0, 0))
>T : Symbol(T, Decl(collectionPatternNoError.ts, 16, 19))

private readonly messageList: MsgConstructor<U>,
>messageList : Symbol(DataProvider.messageList, Decl(collectionPatternNoError.ts, 18, 48))
>MsgConstructor : Symbol(MsgConstructor, Decl(collectionPatternNoError.ts, 0, 0))
>U : Symbol(U, Decl(collectionPatternNoError.ts, 16, 37))

) { }

fetch() {
>fetch : Symbol(DataProvider.fetch, Decl(collectionPatternNoError.ts, 20, 7))

const messageList = fetchMsg(this.messageList);
>messageList : Symbol(messageList, Decl(collectionPatternNoError.ts, 23, 9))
>fetchMsg : Symbol(fetchMsg, Decl(collectionPatternNoError.ts, 10, 1))
>this.messageList : Symbol(DataProvider.messageList, Decl(collectionPatternNoError.ts, 18, 48))
>this : Symbol(DataProvider, Decl(collectionPatternNoError.ts, 14, 1))
>messageList : Symbol(DataProvider.messageList, Decl(collectionPatternNoError.ts, 18, 48))

messageList.methodOnMessageList();
>messageList.methodOnMessageList : Symbol(MessageList.methodOnMessageList, Decl(collectionPatternNoError.ts, 8, 58))
>messageList : Symbol(messageList, Decl(collectionPatternNoError.ts, 23, 9))
>methodOnMessageList : Symbol(MessageList.methodOnMessageList, Decl(collectionPatternNoError.ts, 8, 58))
}
}

// The same bug as the above but using indexed accesses
// (won't surface directly unless unsound indexed access assignments are forbidden)
function f<
>f : Symbol(f, Decl(collectionPatternNoError.ts, 26, 1))

U extends {TType: MessageList<T>},
>U : Symbol(U, Decl(collectionPatternNoError.ts, 30, 11))
>TType : Symbol(TType, Decl(collectionPatternNoError.ts, 31, 13))
>MessageList : Symbol(MessageList, Decl(collectionPatternNoError.ts, 7, 1))
>T : Symbol(T, Decl(collectionPatternNoError.ts, 31, 36))

T extends Message
>T : Symbol(T, Decl(collectionPatternNoError.ts, 31, 36))
>Message : Symbol(Message, Decl(collectionPatternNoError.ts, 2, 1))

>(message: MsgConstructor<T>, messageList: MsgConstructor<U["TType"]>) {
>message : Symbol(message, Decl(collectionPatternNoError.ts, 33, 2))
>MsgConstructor : Symbol(MsgConstructor, Decl(collectionPatternNoError.ts, 0, 0))
>T : Symbol(T, Decl(collectionPatternNoError.ts, 31, 36))
>messageList : Symbol(messageList, Decl(collectionPatternNoError.ts, 33, 29))
>MsgConstructor : Symbol(MsgConstructor, Decl(collectionPatternNoError.ts, 0, 0))
>U : Symbol(U, Decl(collectionPatternNoError.ts, 30, 11))

fetchMsg(messageList).methodOnMessageList();
>fetchMsg(messageList).methodOnMessageList : Symbol(MessageList.methodOnMessageList, Decl(collectionPatternNoError.ts, 8, 58))
>fetchMsg : Symbol(fetchMsg, Decl(collectionPatternNoError.ts, 10, 1))
>messageList : Symbol(messageList, Decl(collectionPatternNoError.ts, 33, 29))
>methodOnMessageList : Symbol(MessageList.methodOnMessageList, Decl(collectionPatternNoError.ts, 8, 58))
}

118 changes: 118 additions & 0 deletions tests/baselines/reference/collectionPatternNoError.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
=== tests/cases/compiler/collectionPatternNoError.ts ===
interface MsgConstructor<T extends Message> {
>MsgConstructor : MsgConstructor<T>
>T : T
>Message : Message

new(data: Array<{}>): T;
>data : {}[]
>Array : T[]
>T : T
}
class Message {
>Message : Message

clone(): this {
>clone : () => this

return this;
>this : this
}
}
interface MessageList<T extends Message> extends Message {
>MessageList : MessageList<T>
>T : T
>Message : Message
>Message : Message

methodOnMessageList(): T[];
>methodOnMessageList : () => T[]
>T : T
}

function fetchMsg<V extends Message>(protoCtor: MsgConstructor<V>): V {
>fetchMsg : <V extends Message>(protoCtor: MsgConstructor<V>) => V
>V : V
>Message : Message
>protoCtor : MsgConstructor<V>
>MsgConstructor : MsgConstructor<T>
>V : V
>V : V

return null!;
>null! : null
>null : null
}

class DataProvider<T extends Message, U extends MessageList<T>> {
>DataProvider : DataProvider<T, U>
>T : T
>Message : Message
>U : U
>MessageList : MessageList<T>
>T : T

constructor(
private readonly message: MsgConstructor<T>,
>message : MsgConstructor<T>
>MsgConstructor : MsgConstructor<T>
>T : T

private readonly messageList: MsgConstructor<U>,
>messageList : MsgConstructor<U>
>MsgConstructor : MsgConstructor<T>
>U : U

) { }

fetch() {
>fetch : () => void

const messageList = fetchMsg(this.messageList);
>messageList : U
>fetchMsg(this.messageList) : U
>fetchMsg : <V extends Message>(protoCtor: MsgConstructor<V>) => V
>this.messageList : MsgConstructor<U>
>this : this
>messageList : MsgConstructor<U>

messageList.methodOnMessageList();
>messageList.methodOnMessageList() : T[]
>messageList.methodOnMessageList : () => T[]
>messageList : U
>methodOnMessageList : () => T[]
}
}

// The same bug as the above but using indexed accesses
// (won't surface directly unless unsound indexed access assignments are forbidden)
function f<
>f : <U extends { TType: MessageList<T>; }, T extends Message>(message: MsgConstructor<T>, messageList: MsgConstructor<U["TType"]>) => void

U extends {TType: MessageList<T>},
>U : U
>TType : MessageList<T>
>MessageList : MessageList<T>
>T : T

T extends Message
>T : T
>Message : Message

>(message: MsgConstructor<T>, messageList: MsgConstructor<U["TType"]>) {
>message : MsgConstructor<T>
>MsgConstructor : MsgConstructor<T>
>T : T
>messageList : MsgConstructor<U["TType"]>
>MsgConstructor : MsgConstructor<T>
>U : U

fetchMsg(messageList).methodOnMessageList();
>fetchMsg(messageList).methodOnMessageList() : T[]
>fetchMsg(messageList).methodOnMessageList : () => T[]
>fetchMsg(messageList) : U["TType"]
>fetchMsg : <V extends Message>(protoCtor: MsgConstructor<V>) => V
>messageList : MsgConstructor<U["TType"]>
>methodOnMessageList : () => T[]
}

2 changes: 0 additions & 2 deletions tests/baselines/reference/fuzzy.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ tests/cases/compiler/fuzzy.ts(21,13): error TS2322: Type '{ anything: number; on
Types of property 'oneI' are incompatible.
Type 'this' is not assignable to type 'I'.
Type 'C' is not assignable to type 'I'.
Property 'alsoWorks' is missing in type 'C'.
tests/cases/compiler/fuzzy.ts(25,20): error TS2352: Type '{ oneI: this; }' cannot be converted to type 'R'.
Property 'anything' is missing in type '{ oneI: this; }'.

Expand Down Expand Up @@ -39,7 +38,6 @@ tests/cases/compiler/fuzzy.ts(25,20): error TS2352: Type '{ oneI: this; }' canno
!!! error TS2322: Types of property 'oneI' are incompatible.
!!! error TS2322: Type 'this' is not assignable to type 'I'.
!!! error TS2322: Type 'C' is not assignable to type 'I'.
!!! error TS2322: Property 'alsoWorks' is missing in type 'C'.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This elaboration disappears as the comparison is now cached correctly and is the same comparison done above for the implements constraint check.

}

worksToo():R {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//// [subclassWithPolymorphicThisIsAssignable.ts]
/* taken from mongoose.Document */
interface Document {
increment(): this;
}

/* our custom model extends the mongoose document */
interface CustomDocument extends Document { }

export class Example<Z extends CustomDocument> {
constructor() {
// types of increment not compatible??
this.test<Z>();
}

public test<Z extends Document>() { }
}


//// [subclassWithPolymorphicThisIsAssignable.js]
"use strict";
exports.__esModule = true;
var Example = /** @class */ (function () {
function Example() {
// types of increment not compatible??
this.test();
}
Example.prototype.test = function () { };
return Example;
}());
exports.Example = Example;
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
=== tests/cases/compiler/subclassWithPolymorphicThisIsAssignable.ts ===
/* taken from mongoose.Document */
interface Document {
>Document : Symbol(Document, Decl(subclassWithPolymorphicThisIsAssignable.ts, 0, 0))

increment(): this;
>increment : Symbol(Document.increment, Decl(subclassWithPolymorphicThisIsAssignable.ts, 1, 20))
}

/* our custom model extends the mongoose document */
interface CustomDocument extends Document { }
>CustomDocument : Symbol(CustomDocument, Decl(subclassWithPolymorphicThisIsAssignable.ts, 3, 1))
>Document : Symbol(Document, Decl(subclassWithPolymorphicThisIsAssignable.ts, 0, 0))

export class Example<Z extends CustomDocument> {
>Example : Symbol(Example, Decl(subclassWithPolymorphicThisIsAssignable.ts, 6, 45))
>Z : Symbol(Z, Decl(subclassWithPolymorphicThisIsAssignable.ts, 8, 21))
>CustomDocument : Symbol(CustomDocument, Decl(subclassWithPolymorphicThisIsAssignable.ts, 3, 1))

constructor() {
// types of increment not compatible??
this.test<Z>();
>this.test : Symbol(Example.test, Decl(subclassWithPolymorphicThisIsAssignable.ts, 12, 5))
>this : Symbol(Example, Decl(subclassWithPolymorphicThisIsAssignable.ts, 6, 45))
>test : Symbol(Example.test, Decl(subclassWithPolymorphicThisIsAssignable.ts, 12, 5))
>Z : Symbol(Z, Decl(subclassWithPolymorphicThisIsAssignable.ts, 8, 21))
}

public test<Z extends Document>() { }
>test : Symbol(Example.test, Decl(subclassWithPolymorphicThisIsAssignable.ts, 12, 5))
>Z : Symbol(Z, Decl(subclassWithPolymorphicThisIsAssignable.ts, 14, 16))
>Document : Symbol(Document, Decl(subclassWithPolymorphicThisIsAssignable.ts, 0, 0))
}

Loading