mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
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:
parent
4c22bf786e
commit
02fe840732
@ -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) {
|
||||
|
||||
67
tests/baselines/reference/collectionPatternNoError.js
Normal file
67
tests/baselines/reference/collectionPatternNoError.js
Normal 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();
|
||||
}
|
||||
112
tests/baselines/reference/collectionPatternNoError.symbols
Normal file
112
tests/baselines/reference/collectionPatternNoError.symbols
Normal 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))
|
||||
}
|
||||
|
||||
118
tests/baselines/reference/collectionPatternNoError.types
Normal file
118
tests/baselines/reference/collectionPatternNoError.types
Normal 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[]
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
36
tests/cases/compiler/collectionPatternNoError.ts
Normal file
36
tests/cases/compiler/collectionPatternNoError.ts
Normal 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();
|
||||
}
|
||||
@ -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>() { }
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user