Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
5 changes: 5 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5595,9 +5595,14 @@ namespace ts {

if (source.flags & TypeFlags.TypeParameter) {
let constraint = getConstraintOfTypeParameter(<TypeParameter>source);

if (!constraint || constraint.flags & TypeFlags.Any) {
constraint = emptyObjectType;
}

// The constraint may need to be further instantiated with its 'this' type.
constraint = getTypeWithThisArgument(constraint);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let me check my understanding: an unconstrained type parameter gets 'any' or '{}' as its constraint, which shouldn't have a this-type, so this is a no-op there. Am I right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yup. I could have introduced an else branch, but I figured that this would be a little simpler to read. I'm open to changing it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No, I think it's fine. Just checking.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You want to instantiate the constraint with source as the this-type parameter, so this line should be:

constraint = getTypeWithThisArgument(constraint, source);

As you have it now you're instantiating the constraint with its own this-type as the this-type parameter. I think the reason it works with the example in the bug is because the this-type of Something is assignable to the this-type of Bar, but it's not the right solution.


// Report constraint errors only if the constraint is not the empty object type
const reportConstraintErrors = reportErrors && constraint !== emptyObjectType;
if (result = isRelatedTo(constraint, target, reportConstraintErrors)) {
Expand Down
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,20): 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: Neither type '{ oneI: this; }' nor type 'R' is assignable to the other.
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: Neither type '{ oneI: this;
!!! 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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why does this go away? We instantiate this as 'C', then somehow we don't notice that alsoWorks is missing now?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm not 100% sure, but I have some thoughts which we can discuss offline.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Here's the result of the offline discussion:
The error's already elaborated at line 26. So now that this is instantiated as C, the compiler knows that it's already reported this error once. (We think :) So it's fine to drop it here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, this looks like a case of an elaboration that was already made and subsequently cached.

worksToo():R {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//// [recurringTypeParamForContainerOfBase01.ts]

interface BoxOfFoo<T extends Foo<T>> {
item: T
}

interface Foo<T extends Foo<T>> {
self: T;
}

interface Bar<T extends Bar<T>> extends Foo<T> {
other: BoxOfFoo<T>;
}

//// [recurringTypeParamForContainerOfBase01.js]


//// [recurringTypeParamForContainerOfBase01.d.ts]
interface BoxOfFoo<T extends Foo<T>> {
item: T;
}
interface Foo<T extends Foo<T>> {
self: T;
}
interface Bar<T extends Bar<T>> extends Foo<T> {
other: BoxOfFoo<T>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
=== tests/cases/conformance/types/typeParameters/recurringTypeParamForContainerOfBase01.ts ===

interface BoxOfFoo<T extends Foo<T>> {
>BoxOfFoo : Symbol(BoxOfFoo, Decl(recurringTypeParamForContainerOfBase01.ts, 0, 0))
>T : Symbol(T, Decl(recurringTypeParamForContainerOfBase01.ts, 1, 19))
>Foo : Symbol(Foo, Decl(recurringTypeParamForContainerOfBase01.ts, 3, 1))
>T : Symbol(T, Decl(recurringTypeParamForContainerOfBase01.ts, 1, 19))

item: T
>item : Symbol(item, Decl(recurringTypeParamForContainerOfBase01.ts, 1, 38))
>T : Symbol(T, Decl(recurringTypeParamForContainerOfBase01.ts, 1, 19))
}

interface Foo<T extends Foo<T>> {
>Foo : Symbol(Foo, Decl(recurringTypeParamForContainerOfBase01.ts, 3, 1))
>T : Symbol(T, Decl(recurringTypeParamForContainerOfBase01.ts, 5, 14))
>Foo : Symbol(Foo, Decl(recurringTypeParamForContainerOfBase01.ts, 3, 1))
>T : Symbol(T, Decl(recurringTypeParamForContainerOfBase01.ts, 5, 14))

self: T;
>self : Symbol(self, Decl(recurringTypeParamForContainerOfBase01.ts, 5, 33))
>T : Symbol(T, Decl(recurringTypeParamForContainerOfBase01.ts, 5, 14))
}

interface Bar<T extends Bar<T>> extends Foo<T> {
>Bar : Symbol(Bar, Decl(recurringTypeParamForContainerOfBase01.ts, 7, 1))
>T : Symbol(T, Decl(recurringTypeParamForContainerOfBase01.ts, 9, 14))
>Bar : Symbol(Bar, Decl(recurringTypeParamForContainerOfBase01.ts, 7, 1))
>T : Symbol(T, Decl(recurringTypeParamForContainerOfBase01.ts, 9, 14))
>Foo : Symbol(Foo, Decl(recurringTypeParamForContainerOfBase01.ts, 3, 1))
>T : Symbol(T, Decl(recurringTypeParamForContainerOfBase01.ts, 9, 14))

other: BoxOfFoo<T>;
>other : Symbol(other, Decl(recurringTypeParamForContainerOfBase01.ts, 9, 48))
>BoxOfFoo : Symbol(BoxOfFoo, Decl(recurringTypeParamForContainerOfBase01.ts, 0, 0))
>T : Symbol(T, Decl(recurringTypeParamForContainerOfBase01.ts, 9, 14))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
=== tests/cases/conformance/types/typeParameters/recurringTypeParamForContainerOfBase01.ts ===

interface BoxOfFoo<T extends Foo<T>> {
>BoxOfFoo : BoxOfFoo<T>
>T : T
>Foo : Foo<T>
>T : T

item: T
>item : T
>T : T
}

interface Foo<T extends Foo<T>> {
>Foo : Foo<T>
>T : T
>Foo : Foo<T>
>T : T

self: T;
>self : T
>T : T
}

interface Bar<T extends Bar<T>> extends Foo<T> {
>Bar : Bar<T>
>T : T
>Bar : Bar<T>
>T : T
>Foo : Foo<T>
>T : T

other: BoxOfFoo<T>;
>other : BoxOfFoo<T>
>BoxOfFoo : BoxOfFoo<T>
>T : T
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//// [thisTypeInBasePropertyAndDerivedContainerOfBase01.ts]

interface BoxOfFoo<T extends Foo> {
item: T
}

interface Foo {
self: this;
}

interface Bar extends Foo {
other: BoxOfFoo<this>;
}

//// [thisTypeInBasePropertyAndDerivedContainerOfBase01.js]


//// [thisTypeInBasePropertyAndDerivedContainerOfBase01.d.ts]
interface BoxOfFoo<T extends Foo> {
item: T;
}
interface Foo {
self: this;
}
interface Bar extends Foo {
other: BoxOfFoo<this>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
=== tests/cases/conformance/types/thisType/thisTypeInBasePropertyAndDerivedContainerOfBase01.ts ===

interface BoxOfFoo<T extends Foo> {
>BoxOfFoo : Symbol(BoxOfFoo, Decl(thisTypeInBasePropertyAndDerivedContainerOfBase01.ts, 0, 0))
>T : Symbol(T, Decl(thisTypeInBasePropertyAndDerivedContainerOfBase01.ts, 1, 19))
>Foo : Symbol(Foo, Decl(thisTypeInBasePropertyAndDerivedContainerOfBase01.ts, 3, 1))

item: T
>item : Symbol(item, Decl(thisTypeInBasePropertyAndDerivedContainerOfBase01.ts, 1, 35))
>T : Symbol(T, Decl(thisTypeInBasePropertyAndDerivedContainerOfBase01.ts, 1, 19))
}

interface Foo {
>Foo : Symbol(Foo, Decl(thisTypeInBasePropertyAndDerivedContainerOfBase01.ts, 3, 1))

self: this;
>self : Symbol(self, Decl(thisTypeInBasePropertyAndDerivedContainerOfBase01.ts, 5, 15))
}

interface Bar extends Foo {
>Bar : Symbol(Bar, Decl(thisTypeInBasePropertyAndDerivedContainerOfBase01.ts, 7, 1))
>Foo : Symbol(Foo, Decl(thisTypeInBasePropertyAndDerivedContainerOfBase01.ts, 3, 1))

other: BoxOfFoo<this>;
>other : Symbol(other, Decl(thisTypeInBasePropertyAndDerivedContainerOfBase01.ts, 9, 27))
>BoxOfFoo : Symbol(BoxOfFoo, Decl(thisTypeInBasePropertyAndDerivedContainerOfBase01.ts, 0, 0))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
=== tests/cases/conformance/types/thisType/thisTypeInBasePropertyAndDerivedContainerOfBase01.ts ===

interface BoxOfFoo<T extends Foo> {
>BoxOfFoo : BoxOfFoo<T>
>T : T
>Foo : Foo

item: T
>item : T
>T : T
}

interface Foo {
>Foo : Foo

self: this;
>self : this
}

interface Bar extends Foo {
>Bar : Bar
>Foo : Foo

other: BoxOfFoo<this>;
>other : BoxOfFoo<this>
>BoxOfFoo : BoxOfFoo<T>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @declaration: true

interface BoxOfFoo<T extends Foo> {
item: T
}

interface Foo {
self: this;
}

interface Bar extends Foo {
other: BoxOfFoo<this>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @declaration: true

interface BoxOfFoo<T extends Foo<T>> {
item: T
}

interface Foo<T extends Foo<T>> {
self: T;
}

interface Bar<T extends Bar<T>> extends Foo<T> {
other: BoxOfFoo<T>;
}