Skip to content

Commit 9f7621c

Browse files
committed
Contextual signatures without thisType return anyType
If a contextual signature is found, if its thisType is undefined, then the contextual type of `this` is now `any`. Previously `checkThisExpression` would keep looking for a different type for `this`. Also update tests to show this new behaviour.
1 parent 88854d2 commit 9f7621c

File tree

5 files changed

+326
-112
lines changed

5 files changed

+326
-112
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8453,7 +8453,7 @@ namespace ts {
84538453
if (isContextSensitiveFunctionOrObjectLiteralMethod(func) && func.kind !== SyntaxKind.ArrowFunction) {
84548454
const contextualSignature = getContextualSignature(func);
84558455
if (contextualSignature) {
8456-
return contextualSignature.thisType;
8456+
return contextualSignature.thisType || anyType;
84578457
}
84588458
else if (getContextualTypeForFunctionLikeDeclaration(func) === anyType) {
84598459
return anyType;
Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,91 @@
11
//// [thisTypeInFunctions2.ts]
2-
interface Arguments {
3-
init?: (this: void) => void;
2+
interface IndexedWithThis {
3+
// this is a workaround for React
4+
init?: (this: this) => void;
45
willDestroy?: (this: any) => void;
56
[propName: string]: number | string | boolean | symbol | undefined | null | {} | ((this: any, ...args:any[]) => any);
67
}
7-
declare function extend(arguments: Arguments): void;
8-
class Mixin {
9-
stuff: number;
8+
interface IndexedWithoutThis {
9+
// this is what React would like to write (and what they write today)
10+
init?: () => void;
11+
willDestroy?: () => void;
12+
[propName: string]: any;
1013
}
14+
interface SimpleInterface {
15+
foo(n: string);
16+
bar(): number;
17+
}
18+
declare function extend1(args: IndexedWithThis): void;
19+
declare function extend2(args: IndexedWithoutThis): void;
20+
declare function simple(arg: SimpleInterface): void;
1121

12-
extend({
22+
extend1({
1323
init() {
14-
this
24+
this // this: IndexedWithThis because of contextual typing.
25+
// this.mine
26+
this.willDestroy
1527
},
1628
mine: 12,
17-
bar() {
18-
this.init();
29+
foo() {
30+
this.url; // this: any because 'foo' matches the string indexer
31+
this.willDestroy;
32+
}
33+
});
34+
extend2({
35+
init() {
36+
this // this: any because the contextual signature of init doesn't specify this' type
37+
this.mine
38+
this.willDestroy
1939
},
40+
mine: 13,
2041
foo() {
21-
this.bar;
22-
this.url
23-
this.handler()
24-
this.baz
42+
this // this: any because of the string indexer
43+
this.mine
2544
this.willDestroy
2645
}
46+
});
47+
48+
simple({
49+
foo(n) {
50+
return n.length + this.bar();
51+
},
52+
bar() {
53+
return 14;
54+
}
2755
})
2856

2957

3058
//// [thisTypeInFunctions2.js]
31-
var Mixin = (function () {
32-
function Mixin() {
33-
}
34-
return Mixin;
35-
}());
36-
extend({
59+
extend1({
3760
init: function () {
38-
this;
61+
this; // this: IndexedWithThis because of contextual typing.
62+
// this.mine
63+
this.willDestroy;
3964
},
4065
mine: 12,
41-
bar: function () {
42-
this.init();
66+
foo: function () {
67+
this.url; // this: any because 'foo' matches the string indexer
68+
this.willDestroy;
69+
}
70+
});
71+
extend2({
72+
init: function () {
73+
this; // this: any because the contextual signature of init doesn't specify this' type
74+
this.mine;
75+
this.willDestroy;
4376
},
77+
mine: 13,
4478
foo: function () {
45-
this.bar;
46-
this.url;
47-
this.handler();
48-
this.baz;
79+
this; // this: any because of the string indexer
80+
this.mine;
4981
this.willDestroy;
5082
}
5183
});
84+
simple({
85+
foo: function (n) {
86+
return n.length + this.bar();
87+
},
88+
bar: function () {
89+
return 14;
90+
}
91+
});
Lines changed: 99 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,124 @@
11
=== tests/cases/conformance/types/thisType/thisTypeInFunctions2.ts ===
2-
interface Arguments {
3-
>Arguments : Symbol(Arguments, Decl(thisTypeInFunctions2.ts, 0, 0))
2+
interface IndexedWithThis {
3+
>IndexedWithThis : Symbol(IndexedWithThis, Decl(thisTypeInFunctions2.ts, 0, 0))
44

5-
init?: (this: void) => void;
6-
>init : Symbol(Arguments.init, Decl(thisTypeInFunctions2.ts, 0, 21))
7-
>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 1, 12))
5+
// this is a workaround for React
6+
init?: (this: this) => void;
7+
>init : Symbol(IndexedWithThis.init, Decl(thisTypeInFunctions2.ts, 0, 27))
8+
>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 2, 12))
89

910
willDestroy?: (this: any) => void;
10-
>willDestroy : Symbol(Arguments.willDestroy, Decl(thisTypeInFunctions2.ts, 1, 32))
11-
>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 2, 19))
11+
>willDestroy : Symbol(IndexedWithThis.willDestroy, Decl(thisTypeInFunctions2.ts, 2, 32))
12+
>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 3, 19))
1213

1314
[propName: string]: number | string | boolean | symbol | undefined | null | {} | ((this: any, ...args:any[]) => any);
14-
>propName : Symbol(propName, Decl(thisTypeInFunctions2.ts, 3, 5))
15-
>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 3, 87))
16-
>args : Symbol(args, Decl(thisTypeInFunctions2.ts, 3, 97))
15+
>propName : Symbol(propName, Decl(thisTypeInFunctions2.ts, 4, 5))
16+
>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 4, 87))
17+
>args : Symbol(args, Decl(thisTypeInFunctions2.ts, 4, 97))
1718
}
18-
declare function extend(arguments: Arguments): void;
19-
>extend : Symbol(extend, Decl(thisTypeInFunctions2.ts, 4, 1))
20-
>arguments : Symbol(arguments, Decl(thisTypeInFunctions2.ts, 5, 24))
21-
>Arguments : Symbol(Arguments, Decl(thisTypeInFunctions2.ts, 0, 0))
19+
interface IndexedWithoutThis {
20+
>IndexedWithoutThis : Symbol(IndexedWithoutThis, Decl(thisTypeInFunctions2.ts, 5, 1))
2221

23-
class Mixin {
24-
>Mixin : Symbol(Mixin, Decl(thisTypeInFunctions2.ts, 5, 52))
22+
// this is what React would like to write (and what they write today)
23+
init?: () => void;
24+
>init : Symbol(IndexedWithoutThis.init, Decl(thisTypeInFunctions2.ts, 6, 30))
2525

26-
stuff: number;
27-
>stuff : Symbol(Mixin.stuff, Decl(thisTypeInFunctions2.ts, 6, 13))
26+
willDestroy?: () => void;
27+
>willDestroy : Symbol(IndexedWithoutThis.willDestroy, Decl(thisTypeInFunctions2.ts, 8, 22))
28+
29+
[propName: string]: any;
30+
>propName : Symbol(propName, Decl(thisTypeInFunctions2.ts, 10, 5))
31+
}
32+
interface SimpleInterface {
33+
>SimpleInterface : Symbol(SimpleInterface, Decl(thisTypeInFunctions2.ts, 11, 1))
34+
35+
foo(n: string);
36+
>foo : Symbol(SimpleInterface.foo, Decl(thisTypeInFunctions2.ts, 12, 27))
37+
>n : Symbol(n, Decl(thisTypeInFunctions2.ts, 13, 8))
38+
39+
bar(): number;
40+
>bar : Symbol(SimpleInterface.bar, Decl(thisTypeInFunctions2.ts, 13, 19))
2841
}
42+
declare function extend1(args: IndexedWithThis): void;
43+
>extend1 : Symbol(extend1, Decl(thisTypeInFunctions2.ts, 15, 1))
44+
>args : Symbol(args, Decl(thisTypeInFunctions2.ts, 16, 25))
45+
>IndexedWithThis : Symbol(IndexedWithThis, Decl(thisTypeInFunctions2.ts, 0, 0))
2946

30-
extend({
31-
>extend : Symbol(extend, Decl(thisTypeInFunctions2.ts, 4, 1))
47+
declare function extend2(args: IndexedWithoutThis): void;
48+
>extend2 : Symbol(extend2, Decl(thisTypeInFunctions2.ts, 16, 54))
49+
>args : Symbol(args, Decl(thisTypeInFunctions2.ts, 17, 25))
50+
>IndexedWithoutThis : Symbol(IndexedWithoutThis, Decl(thisTypeInFunctions2.ts, 5, 1))
51+
52+
declare function simple(arg: SimpleInterface): void;
53+
>simple : Symbol(simple, Decl(thisTypeInFunctions2.ts, 17, 57))
54+
>arg : Symbol(arg, Decl(thisTypeInFunctions2.ts, 18, 24))
55+
>SimpleInterface : Symbol(SimpleInterface, Decl(thisTypeInFunctions2.ts, 11, 1))
56+
57+
extend1({
58+
>extend1 : Symbol(extend1, Decl(thisTypeInFunctions2.ts, 15, 1))
3259

3360
init() {
34-
>init : Symbol(init, Decl(thisTypeInFunctions2.ts, 10, 8))
61+
>init : Symbol(init, Decl(thisTypeInFunctions2.ts, 20, 9))
62+
63+
this // this: IndexedWithThis because of contextual typing.
64+
>this : Symbol(IndexedWithThis, Decl(thisTypeInFunctions2.ts, 0, 0))
65+
66+
// this.mine
67+
this.willDestroy
68+
>this.willDestroy : Symbol(IndexedWithThis.willDestroy, Decl(thisTypeInFunctions2.ts, 2, 32))
69+
>this : Symbol(IndexedWithThis, Decl(thisTypeInFunctions2.ts, 0, 0))
70+
>willDestroy : Symbol(IndexedWithThis.willDestroy, Decl(thisTypeInFunctions2.ts, 2, 32))
3571

36-
this
3772
},
3873
mine: 12,
39-
>mine : Symbol(mine, Decl(thisTypeInFunctions2.ts, 13, 6))
74+
>mine : Symbol(mine, Decl(thisTypeInFunctions2.ts, 25, 6))
4075

41-
bar() {
42-
>bar : Symbol(bar, Decl(thisTypeInFunctions2.ts, 14, 13))
76+
foo() {
77+
>foo : Symbol(foo, Decl(thisTypeInFunctions2.ts, 26, 13))
78+
79+
this.url; // this: any because 'foo' matches the string indexer
80+
this.willDestroy;
81+
}
82+
});
83+
extend2({
84+
>extend2 : Symbol(extend2, Decl(thisTypeInFunctions2.ts, 16, 54))
4385

44-
this.init();
86+
init() {
87+
>init : Symbol(init, Decl(thisTypeInFunctions2.ts, 32, 9))
88+
89+
this // this: any because the contextual signature of init doesn't specify this' type
90+
this.mine
91+
this.willDestroy
4592
},
93+
mine: 13,
94+
>mine : Symbol(mine, Decl(thisTypeInFunctions2.ts, 37, 6))
95+
4696
foo() {
47-
>foo : Symbol(foo, Decl(thisTypeInFunctions2.ts, 17, 6))
97+
>foo : Symbol(foo, Decl(thisTypeInFunctions2.ts, 38, 13))
4898

49-
this.bar;
50-
this.url
51-
this.handler()
52-
this.baz
99+
this // this: any because of the string indexer
100+
this.mine
53101
this.willDestroy
54102
}
103+
});
104+
105+
simple({
106+
>simple : Symbol(simple, Decl(thisTypeInFunctions2.ts, 17, 57))
107+
108+
foo(n) {
109+
>foo : Symbol(foo, Decl(thisTypeInFunctions2.ts, 46, 8))
110+
>n : Symbol(n, Decl(thisTypeInFunctions2.ts, 47, 8))
111+
112+
return n.length + this.bar();
113+
>n.length : Symbol(String.length, Decl(lib.d.ts, --, --))
114+
>n : Symbol(n, Decl(thisTypeInFunctions2.ts, 47, 8))
115+
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
116+
117+
},
118+
bar() {
119+
>bar : Symbol(bar, Decl(thisTypeInFunctions2.ts, 49, 6))
120+
121+
return 14;
122+
}
55123
})
56124

0 commit comments

Comments
 (0)