Skip to content

Commit 48c5890

Browse files
author
Andy Hanson
committed
Don't contextually type this in exports.f = function() { ... }
1 parent 6823eda commit 48c5890

File tree

7 files changed

+82
-22
lines changed

7 files changed

+82
-22
lines changed

src/compiler/checker.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12780,7 +12780,8 @@ namespace ts {
1278012780
}
1278112781
}
1278212782
}
12783-
if (noImplicitThis || isInJavaScriptFile(func)) {
12783+
const inJs = isInJavaScriptFile(func);
12784+
if (noImplicitThis || inJs) {
1278412785
const containingLiteral = getContainingObjectLiteral(func);
1278512786
if (containingLiteral) {
1278612787
// We have an object literal method. Check if the containing object literal has a contextual type
@@ -12807,10 +12808,20 @@ namespace ts {
1280712808
}
1280812809
// In an assignment of the form 'obj.xxx = function(...)' or 'obj[xxx] = function(...)', the
1280912810
// contextual type for 'this' is 'obj'.
12810-
if (func.parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>func.parent).operatorToken.kind === SyntaxKind.EqualsToken) {
12811-
const target = (<BinaryExpression>func.parent).left;
12811+
const { parent } = func;
12812+
if (parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>parent).operatorToken.kind === SyntaxKind.EqualsToken) {
12813+
const target = (<BinaryExpression>parent).left;
1281212814
if (target.kind === SyntaxKind.PropertyAccessExpression || target.kind === SyntaxKind.ElementAccessExpression) {
12813-
return checkExpressionCached((<PropertyAccessExpression | ElementAccessExpression>target).expression);
12815+
const { expression } = target as PropertyAccessExpression | ElementAccessExpression;
12816+
// Don't contextually type `this` as `exports` in `exports.Point = function(x, y) { this.x = x; this.y = y; }`
12817+
if (inJs && isIdentifier(expression)) {
12818+
const sourceFile = getSourceFileOfNode(parent);
12819+
if (sourceFile.commonJsModuleIndicator && getResolvedSymbol(expression) === sourceFile.symbol) {
12820+
return undefined;
12821+
}
12822+
}
12823+
12824+
return checkExpressionCached(expression);
1281412825
}
1281512826
}
1281612827
}

tests/baselines/reference/commonjsAccessExports.symbols

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,22 @@ exports.x;
99
>exports : Symbol("/a", Decl(a.js, 0, 0))
1010
>x : Symbol(x, Decl(a.js, 0, 0))
1111

12+
// Works nested
13+
{
14+
// 'exports' does not provide a contextual type to a function-class
15+
exports.Cls = function() {
16+
>exports.Cls : Symbol(Cls, Decl(a.js, 4, 1))
17+
>exports : Symbol(Cls, Decl(a.js, 4, 1))
18+
>Cls : Symbol(Cls, Decl(a.js, 4, 1))
19+
20+
this.x = 0;
21+
>x : Symbol((Anonymous function).x, Decl(a.js, 6, 30))
22+
}
23+
}
24+
25+
const instance = new exports.Cls();
26+
>instance : Symbol(instance, Decl(a.js, 11, 5))
27+
>exports.Cls : Symbol(Cls, Decl(a.js, 4, 1))
28+
>exports : Symbol("/a", Decl(a.js, 0, 0))
29+
>Cls : Symbol(Cls, Decl(a.js, 4, 1))
30+

tests/baselines/reference/commonjsAccessExports.types

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,29 @@ exports.x;
1111
>exports : typeof "/a"
1212
>x : number
1313

14+
// Works nested
15+
{
16+
// 'exports' does not provide a contextual type to a function-class
17+
exports.Cls = function() {
18+
>exports.Cls = function() { this.x = 0; } : () => void
19+
>exports.Cls : () => void
20+
>exports : typeof "/a"
21+
>Cls : () => void
22+
>function() { this.x = 0; } : () => void
23+
24+
this.x = 0;
25+
>this.x = 0 : 0
26+
>this.x : any
27+
>this : any
28+
>x : any
29+
>0 : 0
30+
}
31+
}
32+
33+
const instance = new exports.Cls();
34+
>instance : { x: number; }
35+
>new exports.Cls() : { x: number; }
36+
>exports.Cls : () => void
37+
>exports : typeof "/a"
38+
>Cls : () => void
39+

tests/baselines/reference/typeFromParamTagForFunction.symbols

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,15 @@ declare var module: any, exports: any;
88
>exports : Symbol(exports, Decl(node.d.ts, 1, 24))
99

1010
=== tests/cases/conformance/salsa/a-ext.js ===
11-
function A() {
11+
exports. A = function() {
12+
>exports. A : Symbol(A, Decl(a-ext.js, 0, 0))
13+
>exports : Symbol(A, Decl(a-ext.js, 0, 0))
1214
>A : Symbol(A, Decl(a-ext.js, 0, 0))
1315

1416
this.x = 1;
15-
>x : Symbol(A.x, Decl(a-ext.js, 0, 14))
17+
>x : Symbol((Anonymous function).x, Decl(a-ext.js, 0, 25))
1618

1719
};
18-
exports.A = A;
19-
>exports.A : Symbol(A, Decl(a-ext.js, 2, 2))
20-
>exports : Symbol(A, Decl(a-ext.js, 2, 2))
21-
>A : Symbol(A, Decl(a-ext.js, 2, 2))
22-
>A : Symbol(A, Decl(a-ext.js, 0, 0))
2320

2421
=== tests/cases/conformance/salsa/a.js ===
2522
const { A } = require("./a-ext");
@@ -31,9 +28,9 @@ const { A } = require("./a-ext");
3128
function a(p) { p.x; }
3229
>a : Symbol(a, Decl(a.js, 0, 33))
3330
>p : Symbol(p, Decl(a.js, 3, 11))
34-
>p.x : Symbol(A.x, Decl(a-ext.js, 0, 14))
31+
>p.x : Symbol((Anonymous function).x, Decl(a-ext.js, 0, 25))
3532
>p : Symbol(p, Decl(a.js, 3, 11))
36-
>x : Symbol(A.x, Decl(a-ext.js, 0, 14))
33+
>x : Symbol((Anonymous function).x, Decl(a-ext.js, 0, 25))
3734

3835
=== tests/cases/conformance/salsa/b-ext.js ===
3936
exports.B = class {

tests/baselines/reference/typeFromParamTagForFunction.types

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ declare var module: any, exports: any;
88
>exports : any
99

1010
=== tests/cases/conformance/salsa/a-ext.js ===
11-
function A() {
11+
exports. A = function() {
12+
>exports. A = function() { this.x = 1;} : () => void
13+
>exports. A : () => void
14+
>exports : typeof "tests/cases/conformance/salsa/a-ext"
1215
>A : () => void
16+
>function() { this.x = 1;} : () => void
1317

1418
this.x = 1;
1519
>this.x = 1 : 1
@@ -19,12 +23,6 @@ function A() {
1923
>1 : 1
2024

2125
};
22-
exports.A = A;
23-
>exports.A = A : () => void
24-
>exports.A : () => void
25-
>exports : typeof "tests/cases/conformance/salsa/a-ext"
26-
>A : () => void
27-
>A : () => void
2826

2927
=== tests/cases/conformance/salsa/a.js ===
3028
const { A } = require("./a-ext");

tests/cases/compiler/commonjsAccessExports.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,13 @@
66
// @Filename: /a.js
77
exports.x = 0;
88
exports.x;
9+
10+
// Works nested
11+
{
12+
// 'exports' does not provide a contextual type to a function-class
13+
exports.Cls = function() {
14+
this.x = 0;
15+
}
16+
}
17+
18+
const instance = new exports.Cls();

tests/cases/conformance/salsa/typeFromParamTagForFunction.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@ declare function require(id: string): any;
77
declare var module: any, exports: any;
88

99
// @filename: a-ext.js
10-
function A() {
10+
exports. A = function() {
1111
this.x = 1;
1212
};
13-
exports.A = A;
1413

1514
// @filename: a.js
1615
const { A } = require("./a-ext");

0 commit comments

Comments
 (0)