Skip to content

Commit 5fbf3b0

Browse files
authored
Don't treat object properties as potential JS contructors without JSDoc class tag (microsoft#49735)
1 parent 382f0c3 commit 5fbf3b0

10 files changed

+324
-5
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31906,12 +31906,15 @@ namespace ts {
3190631906
return false;
3190731907
}
3190831908
const func = isFunctionDeclaration(node) || isFunctionExpression(node) ? node :
31909-
isVariableDeclaration(node) && node.initializer && isFunctionExpression(node.initializer) ? node.initializer :
31909+
(isVariableDeclaration(node) || isPropertyAssignment(node)) && node.initializer && isFunctionExpression(node.initializer) ? node.initializer :
3191031910
undefined;
3191131911
if (func) {
31912-
// If the node has a @class tag, treat it like a constructor.
31912+
// If the node has a @class or @constructor tag, treat it like a constructor.
3191331913
if (getJSDocClassTag(node)) return true;
3191431914

31915+
// If the node is a property of an object literal.
31916+
if (isPropertyAssignment(walkUpParenthesizedExpressions(func.parent))) return false;
31917+
3191531918
// If the symbol of the node has members, treat it like a constructor.
3191631919
const symbol = getSymbolOfNode(func);
3191731920
return !!symbol?.members?.size;

tests/baselines/reference/functionExpressionNames.symbols

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ var o = {
2222
>C : Symbol(C, Decl(b.js, 5, 9))
2323

2424
this.c = 'nested object'
25-
>this.c : Symbol(C.c, Decl(b.js, 6, 20))
26-
>this : Symbol(C, Decl(b.js, 6, 6))
25+
>this : Symbol(o, Decl(b.js, 5, 7))
2726
>c : Symbol(C.c, Decl(b.js, 6, 20))
2827
}
2928
}

tests/baselines/reference/functionExpressionNames.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ var o = {
3131
this.c = 'nested object'
3232
>this.c = 'nested object' : "nested object"
3333
>this.c : any
34-
>this : this
34+
>this : { C: typeof C; }
3535
>c : any
3636
>'nested object' : "nested object"
3737
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
=== tests/cases/compiler/index.js ===
2+
const a1 = {
3+
>a1 : Symbol(a1, Decl(index.js, 0, 5))
4+
5+
foo() {
6+
>foo : Symbol(foo, Decl(index.js, 0, 12))
7+
8+
this.x = 0;
9+
>this : Symbol(a1, Decl(index.js, 0, 10))
10+
>x : Symbol(x, Decl(index.js, 1, 11))
11+
}
12+
}
13+
14+
const a2 = {
15+
>a2 : Symbol(a2, Decl(index.js, 6, 5))
16+
17+
foo: function() {
18+
>foo : Symbol(foo, Decl(index.js, 6, 12))
19+
20+
this.x = 0;
21+
>this : Symbol(a2, Decl(index.js, 6, 10))
22+
>x : Symbol(foo.x, Decl(index.js, 7, 21))
23+
}
24+
}
25+
26+
const b1 = {
27+
>b1 : Symbol(b1, Decl(index.js, 12, 5))
28+
29+
/** @class */
30+
foo() {
31+
>foo : Symbol(foo, Decl(index.js, 12, 12))
32+
33+
this.x = 0;
34+
>this : Symbol(b1, Decl(index.js, 12, 10))
35+
>x : Symbol(x, Decl(index.js, 14, 11))
36+
}
37+
}
38+
39+
const b2 = {
40+
>b2 : Symbol(b2, Decl(index.js, 19, 5))
41+
42+
/** @class */
43+
foo: function() {
44+
>foo : Symbol(foo, Decl(index.js, 19, 12))
45+
46+
this.x = 0;
47+
>this.x : Symbol(foo.x, Decl(index.js, 21, 21))
48+
>this : Symbol(foo, Decl(index.js, 21, 8))
49+
>x : Symbol(foo.x, Decl(index.js, 21, 21))
50+
}
51+
}
52+
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
=== tests/cases/compiler/index.js ===
2+
const a1 = {
3+
>a1 : { foo(): void; }
4+
>{ foo() { this.x = 0; }} : { foo(): void; }
5+
6+
foo() {
7+
>foo : () => void
8+
9+
this.x = 0;
10+
>this.x = 0 : 0
11+
>this.x : any
12+
>this : { foo(): void; }
13+
>x : any
14+
>0 : 0
15+
}
16+
}
17+
18+
const a2 = {
19+
>a2 : { foo: typeof foo; }
20+
>{ foo: function() { this.x = 0; }} : { foo: typeof foo; }
21+
22+
foo: function() {
23+
>foo : typeof foo
24+
>function() { this.x = 0; } : typeof foo
25+
26+
this.x = 0;
27+
>this.x = 0 : 0
28+
>this.x : any
29+
>this : { foo: typeof foo; }
30+
>x : any
31+
>0 : 0
32+
}
33+
}
34+
35+
const b1 = {
36+
>b1 : { foo(): void; }
37+
>{ /** @class */ foo() { this.x = 0; }} : { foo(): void; }
38+
39+
/** @class */
40+
foo() {
41+
>foo : () => void
42+
43+
this.x = 0;
44+
>this.x = 0 : 0
45+
>this.x : any
46+
>this : { foo(): void; }
47+
>x : any
48+
>0 : 0
49+
}
50+
}
51+
52+
const b2 = {
53+
>b2 : { foo: typeof foo; }
54+
>{ /** @class */ foo: function() { this.x = 0; }} : { foo: typeof foo; }
55+
56+
/** @class */
57+
foo: function() {
58+
>foo : typeof foo
59+
>function() { this.x = 0; } : typeof foo
60+
61+
this.x = 0;
62+
>this.x = 0 : 0
63+
>this.x : any
64+
>this : this
65+
>x : any
66+
>0 : 0
67+
}
68+
}
69+
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//// [index.js]
2+
export { }
3+
let obj = {
4+
x: 10,
5+
y: [1],
6+
fun: function() {
7+
this.x = 1
8+
this/*1*/
9+
},
10+
f2: function() {
11+
this.x
12+
this/*2*/
13+
},
14+
f3: (function() {
15+
this.x = 1
16+
this/*3*/
17+
}),
18+
}
19+
20+
21+
//// [index.js]
22+
"use strict";
23+
exports.__esModule = true;
24+
var obj = {
25+
x: 10,
26+
y: [1],
27+
fun: function () {
28+
this.x = 1;
29+
this; /*1*/
30+
},
31+
f2: function () {
32+
this.x;
33+
this; /*2*/
34+
},
35+
f3: (function () {
36+
this.x = 1;
37+
this; /*3*/
38+
})
39+
};
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
=== tests/cases/compiler/index.js ===
2+
export { }
3+
let obj = {
4+
>obj : Symbol(obj, Decl(index.js, 1, 3))
5+
6+
x: 10,
7+
>x : Symbol(x, Decl(index.js, 1, 11))
8+
9+
y: [1],
10+
>y : Symbol(y, Decl(index.js, 2, 8))
11+
12+
fun: function() {
13+
>fun : Symbol(fun, Decl(index.js, 3, 9))
14+
15+
this.x = 1
16+
>this.x : Symbol(x, Decl(index.js, 1, 11))
17+
>this : Symbol(obj, Decl(index.js, 1, 9))
18+
>x : Symbol(fun.x, Decl(index.js, 4, 19))
19+
20+
this/*1*/
21+
>this : Symbol(obj, Decl(index.js, 1, 9))
22+
23+
},
24+
f2: function() {
25+
>f2 : Symbol(f2, Decl(index.js, 7, 4))
26+
27+
this.x
28+
>this.x : Symbol(x, Decl(index.js, 1, 11))
29+
>this : Symbol(obj, Decl(index.js, 1, 9))
30+
>x : Symbol(x, Decl(index.js, 1, 11))
31+
32+
this/*2*/
33+
>this : Symbol(obj, Decl(index.js, 1, 9))
34+
35+
},
36+
f3: (function() {
37+
>f3 : Symbol(f3, Decl(index.js, 11, 4))
38+
39+
this.x = 1
40+
>x : Symbol((Anonymous function).x, Decl(index.js, 12, 19))
41+
42+
this/*3*/
43+
}),
44+
}
45+
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
=== tests/cases/compiler/index.js ===
2+
export { }
3+
let obj = {
4+
>obj : { x: number; y: number[]; fun: typeof fun; f2: () => void; f3: typeof (Anonymous function); }
5+
>{ x: 10, y: [1], fun: function() { this.x = 1 this/*1*/ }, f2: function() { this.x this/*2*/ }, f3: (function() { this.x = 1 this/*3*/ }),} : { x: number; y: number[]; fun: typeof fun; f2: () => void; f3: typeof (Anonymous function); }
6+
7+
x: 10,
8+
>x : number
9+
>10 : 10
10+
11+
y: [1],
12+
>y : number[]
13+
>[1] : number[]
14+
>1 : 1
15+
16+
fun: function() {
17+
>fun : typeof fun
18+
>function() { this.x = 1 this/*1*/ } : typeof fun
19+
20+
this.x = 1
21+
>this.x = 1 : 1
22+
>this.x : number
23+
>this : { x: number; y: number[]; fun: typeof fun; f2: () => void; f3: typeof (Anonymous function); }
24+
>x : number
25+
>1 : 1
26+
27+
this/*1*/
28+
>this : { x: number; y: number[]; fun: typeof fun; f2: () => void; f3: typeof (Anonymous function); }
29+
30+
},
31+
f2: function() {
32+
>f2 : () => void
33+
>function() { this.x this/*2*/ } : () => void
34+
35+
this.x
36+
>this.x : number
37+
>this : { x: number; y: number[]; fun: typeof fun; f2: () => void; f3: typeof (Anonymous function); }
38+
>x : number
39+
40+
this/*2*/
41+
>this : { x: number; y: number[]; fun: typeof fun; f2: () => void; f3: typeof (Anonymous function); }
42+
43+
},
44+
f3: (function() {
45+
>f3 : typeof (Anonymous function)
46+
>(function() { this.x = 1 this/*3*/ }) : typeof (Anonymous function)
47+
>function() { this.x = 1 this/*3*/ } : typeof (Anonymous function)
48+
49+
this.x = 1
50+
>this.x = 1 : 1
51+
>this.x : any
52+
>this : any
53+
>x : any
54+
>1 : 1
55+
56+
this/*3*/
57+
>this : any
58+
59+
}),
60+
}
61+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// @allowJs: true
2+
// @noEmit: true
3+
// @checkJs: true
4+
5+
// @filename: index.js
6+
const a1 = {
7+
foo() {
8+
this.x = 0;
9+
}
10+
}
11+
12+
const a2 = {
13+
foo: function() {
14+
this.x = 0;
15+
}
16+
}
17+
18+
const b1 = {
19+
/** @class */
20+
foo() {
21+
this.x = 0;
22+
}
23+
}
24+
25+
const b2 = {
26+
/** @class */
27+
foo: function() {
28+
this.x = 0;
29+
}
30+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// @allowJs: true
2+
// @outDir: out
3+
4+
// @filename: index.js
5+
export { }
6+
let obj = {
7+
x: 10,
8+
y: [1],
9+
fun: function() {
10+
this.x = 1
11+
this/*1*/
12+
},
13+
f2: function() {
14+
this.x
15+
this/*2*/
16+
},
17+
f3: (function() {
18+
this.x = 1
19+
this/*3*/
20+
}),
21+
}

0 commit comments

Comments
 (0)