diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3e3c3964b9455..dd08e02cd7e45 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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) { diff --git a/tests/baselines/reference/collectionPatternNoError.js b/tests/baselines/reference/collectionPatternNoError.js new file mode 100644 index 0000000000000..eff09af026778 --- /dev/null +++ b/tests/baselines/reference/collectionPatternNoError.js @@ -0,0 +1,67 @@ +//// [collectionPatternNoError.ts] +interface MsgConstructor { + new(data: Array<{}>): T; +} +class Message { + clone(): this { + return this; + } +} +interface MessageList extends Message { + methodOnMessageList(): T[]; +} + +function fetchMsg(protoCtor: MsgConstructor): V { + return null!; +} + +class DataProvider> { + constructor( + private readonly message: MsgConstructor, + private readonly messageList: MsgConstructor, + ) { } + + 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 extends Message +>(message: MsgConstructor, messageList: MsgConstructor) { + 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(); +} diff --git a/tests/baselines/reference/collectionPatternNoError.symbols b/tests/baselines/reference/collectionPatternNoError.symbols new file mode 100644 index 0000000000000..be9bffdac6d74 --- /dev/null +++ b/tests/baselines/reference/collectionPatternNoError.symbols @@ -0,0 +1,112 @@ +=== tests/cases/compiler/collectionPatternNoError.ts === +interface MsgConstructor { +>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 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(protoCtor: MsgConstructor): 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> { +>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, +>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, +>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}, +>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, messageList: MsgConstructor) { +>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)) +} + diff --git a/tests/baselines/reference/collectionPatternNoError.types b/tests/baselines/reference/collectionPatternNoError.types new file mode 100644 index 0000000000000..c11af4e11d93b --- /dev/null +++ b/tests/baselines/reference/collectionPatternNoError.types @@ -0,0 +1,118 @@ +=== tests/cases/compiler/collectionPatternNoError.ts === +interface MsgConstructor { +>MsgConstructor : MsgConstructor +>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 extends Message { +>MessageList : MessageList +>T : T +>Message : Message +>Message : Message + + methodOnMessageList(): T[]; +>methodOnMessageList : () => T[] +>T : T +} + +function fetchMsg(protoCtor: MsgConstructor): V { +>fetchMsg : (protoCtor: MsgConstructor) => V +>V : V +>Message : Message +>protoCtor : MsgConstructor +>MsgConstructor : MsgConstructor +>V : V +>V : V + + return null!; +>null! : null +>null : null +} + +class DataProvider> { +>DataProvider : DataProvider +>T : T +>Message : Message +>U : U +>MessageList : MessageList +>T : T + + constructor( + private readonly message: MsgConstructor, +>message : MsgConstructor +>MsgConstructor : MsgConstructor +>T : T + + private readonly messageList: MsgConstructor, +>messageList : MsgConstructor +>MsgConstructor : MsgConstructor +>U : U + + ) { } + + fetch() { +>fetch : () => void + + const messageList = fetchMsg(this.messageList); +>messageList : U +>fetchMsg(this.messageList) : U +>fetchMsg : (protoCtor: MsgConstructor) => V +>this.messageList : MsgConstructor +>this : this +>messageList : MsgConstructor + + 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 : ; }, T extends Message>(message: MsgConstructor, messageList: MsgConstructor) => void + + U extends {TType: MessageList}, +>U : U +>TType : MessageList +>MessageList : MessageList +>T : T + + T extends Message +>T : T +>Message : Message + +>(message: MsgConstructor, messageList: MsgConstructor) { +>message : MsgConstructor +>MsgConstructor : MsgConstructor +>T : T +>messageList : MsgConstructor +>MsgConstructor : MsgConstructor +>U : U + + fetchMsg(messageList).methodOnMessageList(); +>fetchMsg(messageList).methodOnMessageList() : T[] +>fetchMsg(messageList).methodOnMessageList : () => T[] +>fetchMsg(messageList) : U["TType"] +>fetchMsg : (protoCtor: MsgConstructor) => V +>messageList : MsgConstructor +>methodOnMessageList : () => T[] +} + diff --git a/tests/baselines/reference/fuzzy.errors.txt b/tests/baselines/reference/fuzzy.errors.txt index b32f225a33e31..c3f05fd7f2f04 100644 --- a/tests/baselines/reference/fuzzy.errors.txt +++ b/tests/baselines/reference/fuzzy.errors.txt @@ -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; }'. @@ -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'. } worksToo():R { diff --git a/tests/baselines/reference/subclassWithPolymorphicThisIsAssignable.js b/tests/baselines/reference/subclassWithPolymorphicThisIsAssignable.js new file mode 100644 index 0000000000000..514257ca5b96c --- /dev/null +++ b/tests/baselines/reference/subclassWithPolymorphicThisIsAssignable.js @@ -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 { + constructor() { + // types of increment not compatible?? + this.test(); + } + + public test() { } +} + + +//// [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; diff --git a/tests/baselines/reference/subclassWithPolymorphicThisIsAssignable.symbols b/tests/baselines/reference/subclassWithPolymorphicThisIsAssignable.symbols new file mode 100644 index 0000000000000..9a0ff766abcea --- /dev/null +++ b/tests/baselines/reference/subclassWithPolymorphicThisIsAssignable.symbols @@ -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 { +>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(); +>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() { } +>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)) +} + diff --git a/tests/baselines/reference/subclassWithPolymorphicThisIsAssignable.types b/tests/baselines/reference/subclassWithPolymorphicThisIsAssignable.types new file mode 100644 index 0000000000000..d84570d2e0ff2 --- /dev/null +++ b/tests/baselines/reference/subclassWithPolymorphicThisIsAssignable.types @@ -0,0 +1,35 @@ +=== tests/cases/compiler/subclassWithPolymorphicThisIsAssignable.ts === +/* taken from mongoose.Document */ +interface Document { +>Document : Document + + increment(): this; +>increment : () => this +} + +/* our custom model extends the mongoose document */ +interface CustomDocument extends Document { } +>CustomDocument : CustomDocument +>Document : Document + +export class Example { +>Example : Example +>Z : Z +>CustomDocument : CustomDocument + + constructor() { + // types of increment not compatible?? + this.test(); +>this.test() : void +>this.test : () => void +>this : this +>test : () => void +>Z : Z + } + + public test() { } +>test : () => void +>Z : Z +>Document : Document +} + diff --git a/tests/cases/compiler/collectionPatternNoError.ts b/tests/cases/compiler/collectionPatternNoError.ts new file mode 100644 index 0000000000000..83fa6808d5b16 --- /dev/null +++ b/tests/cases/compiler/collectionPatternNoError.ts @@ -0,0 +1,36 @@ +interface MsgConstructor { + new(data: Array<{}>): T; +} +class Message { + clone(): this { + return this; + } +} +interface MessageList extends Message { + methodOnMessageList(): T[]; +} + +function fetchMsg(protoCtor: MsgConstructor): V { + return null!; +} + +class DataProvider> { + constructor( + private readonly message: MsgConstructor, + private readonly messageList: MsgConstructor, + ) { } + + 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 extends Message +>(message: MsgConstructor, messageList: MsgConstructor) { + fetchMsg(messageList).methodOnMessageList(); +} diff --git a/tests/cases/compiler/subclassWithPolymorphicThisIsAssignable.ts b/tests/cases/compiler/subclassWithPolymorphicThisIsAssignable.ts new file mode 100644 index 0000000000000..acd3ac13476d8 --- /dev/null +++ b/tests/cases/compiler/subclassWithPolymorphicThisIsAssignable.ts @@ -0,0 +1,16 @@ +/* taken from mongoose.Document */ +interface Document { + increment(): this; +} + +/* our custom model extends the mongoose document */ +interface CustomDocument extends Document { } + +export class Example { + constructor() { + // types of increment not compatible?? + this.test(); + } + + public test() { } +}