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

* Get constraint with this argument of the type parameter for comparisons

* Also instantiate indexed accesses

* Add much simpler test
This commit is contained in:
Wesley Wigham 2018-05-18 18:30:23 -07:00 committed by GitHub
parent 4c22bf786e
commit 02fe840732
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 455 additions and 5 deletions

View File

@ -10954,9 +10954,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) {

View File

@ -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();
}

View File

@ -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))
}

View File

@ -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[]
}

View File

@ -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 {

View File

@ -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;

View File

@ -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))
}

View File

@ -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<Z extends CustomDocument> {
>Example : Example<Z>
>Z : Z
>CustomDocument : CustomDocument
constructor() {
// types of increment not compatible??
this.test<Z>();
>this.test<Z>() : void
>this.test : <Z extends Document>() => void
>this : this
>test : <Z extends Document>() => void
>Z : Z
}
public test<Z extends Document>() { }
>test : <Z extends Document>() => void
>Z : Z
>Document : Document
}

View File

@ -0,0 +1,36 @@
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();
}

View File

@ -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<Z extends CustomDocument> {
constructor() {
// types of increment not compatible??
this.test<Z>();
}
public test<Z extends Document>() { }
}