Skip to content

Commit d1c7a62

Browse files
committed
Merge pull request #5552 from Microsoft/thisAndConstraints
Instantiate constraints with type parameter as 'this'
2 parents aa39994 + d52455f commit d1c7a62

12 files changed

+292
-124
lines changed

src/compiler/checker.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2990,7 +2990,7 @@ namespace ts {
29902990
(<GenericType>type).typeArguments = type.typeParameters;
29912991
type.thisType = <TypeParameter>createType(TypeFlags.TypeParameter | TypeFlags.ThisType);
29922992
type.thisType.symbol = symbol;
2993-
type.thisType.constraint = getTypeWithThisArgument(type);
2993+
type.thisType.constraint = type;
29942994
}
29952995
}
29962996
return <InterfaceType>links.declaredType;
@@ -3533,19 +3533,29 @@ namespace ts {
35333533
return type.flags & TypeFlags.UnionOrIntersection ? getPropertiesOfUnionOrIntersectionType(<UnionType>type) : getPropertiesOfObjectType(type);
35343534
}
35353535

3536+
/**
3537+
* The apparent type of a type parameter is the base constraint instantiated with the type parameter
3538+
* as the type argument for the 'this' type.
3539+
*/
3540+
function getApparentTypeOfTypeParameter(type: TypeParameter) {
3541+
if (!type.resolvedApparentType) {
3542+
let constraintType = getConstraintOfTypeParameter(type);
3543+
while (constraintType && constraintType.flags & TypeFlags.TypeParameter) {
3544+
constraintType = getConstraintOfTypeParameter(<TypeParameter>constraintType);
3545+
}
3546+
type.resolvedApparentType = getTypeWithThisArgument(constraintType || emptyObjectType, type);
3547+
}
3548+
return type.resolvedApparentType;
3549+
}
3550+
35363551
/**
35373552
* For a type parameter, return the base constraint of the type parameter. For the string, number,
35383553
* boolean, and symbol primitive types, return the corresponding object types. Otherwise return the
35393554
* type itself. Note that the apparent type of a union type is the union type itself.
35403555
*/
35413556
function getApparentType(type: Type): Type {
35423557
if (type.flags & TypeFlags.TypeParameter) {
3543-
do {
3544-
type = getConstraintOfTypeParameter(<TypeParameter>type);
3545-
} while (type && type.flags & TypeFlags.TypeParameter);
3546-
if (!type) {
3547-
type = emptyObjectType;
3548-
}
3558+
type = getApparentTypeOfTypeParameter(<TypeParameter>type);
35493559
}
35503560
if (type.flags & TypeFlags.StringLike) {
35513561
type = globalStringType;

src/compiler/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1956,6 +1956,8 @@ namespace ts {
19561956
target?: TypeParameter; // Instantiation target
19571957
/* @internal */
19581958
mapper?: TypeMapper; // Instantiation mapper
1959+
/* @internal */
1960+
resolvedApparentType: Type;
19591961
}
19601962

19611963
export const enum SignatureKind {

tests/baselines/reference/fuzzy.errors.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ tests/cases/compiler/fuzzy.ts(21,20): error TS2322: Type '{ anything: number; on
44
Types of property 'oneI' are incompatible.
55
Type 'this' is not assignable to type 'I'.
66
Type 'C' is not assignable to type 'I'.
7+
Property 'alsoWorks' is missing in type 'C'.
78
tests/cases/compiler/fuzzy.ts(25,20): error TS2352: Neither type '{ oneI: this; }' nor type 'R' is assignable to the other.
89
Property 'anything' is missing in type '{ oneI: this; }'.
910

@@ -38,6 +39,7 @@ tests/cases/compiler/fuzzy.ts(25,20): error TS2352: Neither type '{ oneI: this;
3839
!!! error TS2322: Types of property 'oneI' are incompatible.
3940
!!! error TS2322: Type 'this' is not assignable to type 'I'.
4041
!!! error TS2322: Type 'C' is not assignable to type 'I'.
42+
!!! error TS2322: Property 'alsoWorks' is missing in type 'C'.
4143
}
4244

4345
worksToo():R {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//// [thisTypeAndConstraints.ts]
2+
class A {
3+
self() {
4+
return this;
5+
}
6+
}
7+
8+
function f<T extends A>(x: T) {
9+
function g<U extends T>(x: U) {
10+
x = x.self();
11+
}
12+
x = x.self();
13+
}
14+
15+
class B<T extends A> {
16+
foo(x: T) {
17+
x = x.self();
18+
}
19+
bar<U extends T>(x: U) {
20+
x = x.self();
21+
}
22+
}
23+
24+
25+
//// [thisTypeAndConstraints.js]
26+
var A = (function () {
27+
function A() {
28+
}
29+
A.prototype.self = function () {
30+
return this;
31+
};
32+
return A;
33+
})();
34+
function f(x) {
35+
function g(x) {
36+
x = x.self();
37+
}
38+
x = x.self();
39+
}
40+
var B = (function () {
41+
function B() {
42+
}
43+
B.prototype.foo = function (x) {
44+
x = x.self();
45+
};
46+
B.prototype.bar = function (x) {
47+
x = x.self();
48+
};
49+
return B;
50+
})();
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
=== tests/cases/conformance/types/thisType/thisTypeAndConstraints.ts ===
2+
class A {
3+
>A : Symbol(A, Decl(thisTypeAndConstraints.ts, 0, 0))
4+
5+
self() {
6+
>self : Symbol(self, Decl(thisTypeAndConstraints.ts, 0, 9))
7+
8+
return this;
9+
>this : Symbol(A, Decl(thisTypeAndConstraints.ts, 0, 0))
10+
}
11+
}
12+
13+
function f<T extends A>(x: T) {
14+
>f : Symbol(f, Decl(thisTypeAndConstraints.ts, 4, 1))
15+
>T : Symbol(T, Decl(thisTypeAndConstraints.ts, 6, 11))
16+
>A : Symbol(A, Decl(thisTypeAndConstraints.ts, 0, 0))
17+
>x : Symbol(x, Decl(thisTypeAndConstraints.ts, 6, 24))
18+
>T : Symbol(T, Decl(thisTypeAndConstraints.ts, 6, 11))
19+
20+
function g<U extends T>(x: U) {
21+
>g : Symbol(g, Decl(thisTypeAndConstraints.ts, 6, 31))
22+
>U : Symbol(U, Decl(thisTypeAndConstraints.ts, 7, 15))
23+
>T : Symbol(T, Decl(thisTypeAndConstraints.ts, 6, 11))
24+
>x : Symbol(x, Decl(thisTypeAndConstraints.ts, 7, 28))
25+
>U : Symbol(U, Decl(thisTypeAndConstraints.ts, 7, 15))
26+
27+
x = x.self();
28+
>x : Symbol(x, Decl(thisTypeAndConstraints.ts, 7, 28))
29+
>x.self : Symbol(A.self, Decl(thisTypeAndConstraints.ts, 0, 9))
30+
>x : Symbol(x, Decl(thisTypeAndConstraints.ts, 7, 28))
31+
>self : Symbol(A.self, Decl(thisTypeAndConstraints.ts, 0, 9))
32+
}
33+
x = x.self();
34+
>x : Symbol(x, Decl(thisTypeAndConstraints.ts, 6, 24))
35+
>x.self : Symbol(A.self, Decl(thisTypeAndConstraints.ts, 0, 9))
36+
>x : Symbol(x, Decl(thisTypeAndConstraints.ts, 6, 24))
37+
>self : Symbol(A.self, Decl(thisTypeAndConstraints.ts, 0, 9))
38+
}
39+
40+
class B<T extends A> {
41+
>B : Symbol(B, Decl(thisTypeAndConstraints.ts, 11, 1))
42+
>T : Symbol(T, Decl(thisTypeAndConstraints.ts, 13, 8))
43+
>A : Symbol(A, Decl(thisTypeAndConstraints.ts, 0, 0))
44+
45+
foo(x: T) {
46+
>foo : Symbol(foo, Decl(thisTypeAndConstraints.ts, 13, 22))
47+
>x : Symbol(x, Decl(thisTypeAndConstraints.ts, 14, 8))
48+
>T : Symbol(T, Decl(thisTypeAndConstraints.ts, 13, 8))
49+
50+
x = x.self();
51+
>x : Symbol(x, Decl(thisTypeAndConstraints.ts, 14, 8))
52+
>x.self : Symbol(A.self, Decl(thisTypeAndConstraints.ts, 0, 9))
53+
>x : Symbol(x, Decl(thisTypeAndConstraints.ts, 14, 8))
54+
>self : Symbol(A.self, Decl(thisTypeAndConstraints.ts, 0, 9))
55+
}
56+
bar<U extends T>(x: U) {
57+
>bar : Symbol(bar, Decl(thisTypeAndConstraints.ts, 16, 5))
58+
>U : Symbol(U, Decl(thisTypeAndConstraints.ts, 17, 8))
59+
>T : Symbol(T, Decl(thisTypeAndConstraints.ts, 13, 8))
60+
>x : Symbol(x, Decl(thisTypeAndConstraints.ts, 17, 21))
61+
>U : Symbol(U, Decl(thisTypeAndConstraints.ts, 17, 8))
62+
63+
x = x.self();
64+
>x : Symbol(x, Decl(thisTypeAndConstraints.ts, 17, 21))
65+
>x.self : Symbol(A.self, Decl(thisTypeAndConstraints.ts, 0, 9))
66+
>x : Symbol(x, Decl(thisTypeAndConstraints.ts, 17, 21))
67+
>self : Symbol(A.self, Decl(thisTypeAndConstraints.ts, 0, 9))
68+
}
69+
}
70+
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
=== tests/cases/conformance/types/thisType/thisTypeAndConstraints.ts ===
2+
class A {
3+
>A : A
4+
5+
self() {
6+
>self : () => this
7+
8+
return this;
9+
>this : this
10+
}
11+
}
12+
13+
function f<T extends A>(x: T) {
14+
>f : <T extends A>(x: T) => void
15+
>T : T
16+
>A : A
17+
>x : T
18+
>T : T
19+
20+
function g<U extends T>(x: U) {
21+
>g : <U extends T>(x: U) => void
22+
>U : U
23+
>T : T
24+
>x : U
25+
>U : U
26+
27+
x = x.self();
28+
>x = x.self() : U
29+
>x : U
30+
>x.self() : U
31+
>x.self : () => U
32+
>x : U
33+
>self : () => U
34+
}
35+
x = x.self();
36+
>x = x.self() : T
37+
>x : T
38+
>x.self() : T
39+
>x.self : () => T
40+
>x : T
41+
>self : () => T
42+
}
43+
44+
class B<T extends A> {
45+
>B : B<T>
46+
>T : T
47+
>A : A
48+
49+
foo(x: T) {
50+
>foo : (x: T) => void
51+
>x : T
52+
>T : T
53+
54+
x = x.self();
55+
>x = x.self() : T
56+
>x : T
57+
>x.self() : T
58+
>x.self : () => T
59+
>x : T
60+
>self : () => T
61+
}
62+
bar<U extends T>(x: U) {
63+
>bar : <U extends T>(x: U) => void
64+
>U : U
65+
>T : T
66+
>x : U
67+
>U : U
68+
69+
x = x.self();
70+
>x = x.self() : U
71+
>x : U
72+
>x.self() : U
73+
>x.self : () => U
74+
>x : U
75+
>self : () => U
76+
}
77+
}
78+

tests/baselines/reference/thisTypeInClasses.errors.txt

Lines changed: 0 additions & 57 deletions
This file was deleted.

tests/baselines/reference/thisTypeInClasses.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
class C1 {
33
x: this;
44
f(x: this): this { return undefined; }
5-
constructor(x: this) { }
65
}
76

87
class C2 {
@@ -53,7 +52,7 @@ class C5 {
5352

5453
//// [thisTypeInClasses.js]
5554
var C1 = (function () {
56-
function C1(x) {
55+
function C1() {
5756
}
5857
C1.prototype.f = function (x) { return undefined; };
5958
return C1;

0 commit comments

Comments
 (0)