Skip to content

Commit 3ade89c

Browse files
authored
Merge pull request #16178 from Microsoft/master-fix13306
[Master] Fix #13306 recognize @type on property assignment
2 parents aac7fb7 + 9df2931 commit 3ade89c

9 files changed

+387
-10
lines changed

src/compiler/checker.ts

+11-6
Original file line numberDiff line numberDiff line change
@@ -12693,12 +12693,6 @@ namespace ts {
1269312693
if (typeNode) {
1269412694
return getTypeFromTypeNode(typeNode);
1269512695
}
12696-
if (isInJavaScriptFile(declaration)) {
12697-
const jsDocType = getTypeForDeclarationFromJSDocComment(declaration);
12698-
if (jsDocType) {
12699-
return jsDocType;
12700-
}
12701-
}
1270212696
if (declaration.kind === SyntaxKind.Parameter) {
1270312697
const type = getContextuallyTypedParameterType(<ParameterDeclaration>declaration);
1270412698
if (type) {
@@ -13306,6 +13300,7 @@ namespace ts {
1330613300
let patternWithComputedProperties = false;
1330713301
let hasComputedStringProperty = false;
1330813302
let hasComputedNumberProperty = false;
13303+
const isInJSFile = isInJavaScriptFile(node);
1330913304

1331013305
let offset = 0;
1331113306
for (let i = 0; i < node.properties.length; i++) {
@@ -13314,6 +13309,11 @@ namespace ts {
1331413309
if (memberDecl.kind === SyntaxKind.PropertyAssignment ||
1331513310
memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment ||
1331613311
isObjectLiteralMethod(memberDecl)) {
13312+
let jsdocType: Type;
13313+
if (isInJSFile) {
13314+
jsdocType = getTypeForDeclarationFromJSDocComment(memberDecl);
13315+
}
13316+
1331713317
let type: Type;
1331813318
if (memberDecl.kind === SyntaxKind.PropertyAssignment) {
1331913319
type = checkPropertyAssignment(<PropertyAssignment>memberDecl, checkMode);
@@ -13326,6 +13326,11 @@ namespace ts {
1332613326
type = checkExpressionForMutableLocation((<ShorthandPropertyAssignment>memberDecl).name, checkMode);
1332713327
}
1332813328

13329+
if (jsdocType) {
13330+
checkTypeAssignableTo(type, jsdocType, memberDecl);
13331+
type = jsdocType;
13332+
}
13333+
1332913334
typeFlags |= type.flags;
1333013335
const prop = createSymbol(SymbolFlags.Property | member.flags, member.name);
1333113336
if (inDestructuringPattern) {

src/compiler/utilities.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -2664,11 +2664,11 @@ namespace ts {
26642664
* Gets the effective type annotation of a variable, parameter, or property. If the node was
26652665
* parsed in a JavaScript file, gets the type annotation from JSDoc.
26662666
*/
2667-
export function getEffectiveTypeAnnotationNode(node: VariableLikeDeclaration): TypeNode {
2667+
export function getEffectiveTypeAnnotationNode(node: VariableLikeDeclaration): TypeNode | undefined {
26682668
if (node.type) {
26692669
return node.type;
26702670
}
2671-
if (node.flags & NodeFlags.JavaScriptFile) {
2671+
if (isInJavaScriptFile(node)) {
26722672
return getJSDocType(node);
26732673
}
26742674
}
@@ -2677,11 +2677,11 @@ namespace ts {
26772677
* Gets the effective return type annotation of a signature. If the node was parsed in a
26782678
* JavaScript file, gets the return type annotation from JSDoc.
26792679
*/
2680-
export function getEffectiveReturnTypeNode(node: SignatureDeclaration): TypeNode {
2680+
export function getEffectiveReturnTypeNode(node: SignatureDeclaration): TypeNode | undefined {
26812681
if (node.type) {
26822682
return node.type;
26832683
}
2684-
if (node.flags & NodeFlags.JavaScriptFile) {
2684+
if (isInJavaScriptFile(node)) {
26852685
return getJSDocReturnType(node);
26862686
}
26872687
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//// [0.js]
2+
// @ts-check
3+
var lol = "hello Lol"
4+
const obj = {
5+
/** @type {string|undefined} */
6+
foo: undefined,
7+
/** @type {string|undefined} */
8+
bar: "42",
9+
/** @type {function(number): number} */
10+
method1(n1) {
11+
return n1 + 42;
12+
},
13+
/** @type {string} */
14+
lol,
15+
/** @type {number} */
16+
['b' + 'ar1']: 42,
17+
/** @type {function(number): number} */
18+
arrowFunc: (num) => num + 42
19+
}
20+
obj.foo = 'string'
21+
obj.lol
22+
obj.bar = undefined;
23+
var k = obj.method1(0);
24+
obj.bar1 = "42";
25+
obj.arrowFunc(0);
26+
27+
//// [0.js]
28+
// @ts-check
29+
var lol = "hello Lol";
30+
var obj = (_a = {
31+
/** @type {string|undefined} */
32+
foo: undefined,
33+
/** @type {string|undefined} */
34+
bar: "42",
35+
/** @type {function(number): number} */
36+
method1: function (n1) {
37+
return n1 + 42;
38+
},
39+
/** @type {string} */
40+
lol: lol
41+
},
42+
/** @type {number} */
43+
_a['b' + 'ar1'] = 42,
44+
/** @type {function(number): number} */
45+
_a.arrowFunc = function (num) { return num + 42; },
46+
_a);
47+
obj.foo = 'string';
48+
obj.lol;
49+
obj.bar = undefined;
50+
var k = obj.method1(0);
51+
obj.bar1 = "42";
52+
obj.arrowFunc(0);
53+
var _a;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
=== tests/cases/conformance/jsdoc/0.js ===
2+
// @ts-check
3+
var lol = "hello Lol"
4+
>lol : Symbol(lol, Decl(0.js, 1, 3))
5+
6+
const obj = {
7+
>obj : Symbol(obj, Decl(0.js, 2, 5))
8+
9+
/** @type {string|undefined} */
10+
foo: undefined,
11+
>foo : Symbol(foo, Decl(0.js, 2, 13))
12+
>undefined : Symbol(undefined)
13+
14+
/** @type {string|undefined} */
15+
bar: "42",
16+
>bar : Symbol(bar, Decl(0.js, 4, 17))
17+
18+
/** @type {function(number): number} */
19+
method1(n1) {
20+
>method1 : Symbol(method1, Decl(0.js, 6, 12))
21+
>n1 : Symbol(n1, Decl(0.js, 8, 10))
22+
23+
return n1 + 42;
24+
>n1 : Symbol(n1, Decl(0.js, 8, 10))
25+
26+
},
27+
/** @type {string} */
28+
lol,
29+
>lol : Symbol(lol, Decl(0.js, 10, 4))
30+
31+
/** @type {number} */
32+
['b' + 'ar1']: 42,
33+
/** @type {function(number): number} */
34+
arrowFunc: (num) => num + 42
35+
>arrowFunc : Symbol(arrowFunc, Decl(0.js, 14, 20))
36+
>num : Symbol(num, Decl(0.js, 16, 14))
37+
>num : Symbol(num, Decl(0.js, 16, 14))
38+
}
39+
obj.foo = 'string'
40+
>obj.foo : Symbol(foo, Decl(0.js, 2, 13))
41+
>obj : Symbol(obj, Decl(0.js, 2, 5))
42+
>foo : Symbol(foo, Decl(0.js, 2, 13))
43+
44+
obj.lol
45+
>obj.lol : Symbol(lol, Decl(0.js, 10, 4))
46+
>obj : Symbol(obj, Decl(0.js, 2, 5))
47+
>lol : Symbol(lol, Decl(0.js, 10, 4))
48+
49+
obj.bar = undefined;
50+
>obj.bar : Symbol(bar, Decl(0.js, 4, 17))
51+
>obj : Symbol(obj, Decl(0.js, 2, 5))
52+
>bar : Symbol(bar, Decl(0.js, 4, 17))
53+
>undefined : Symbol(undefined)
54+
55+
var k = obj.method1(0);
56+
>k : Symbol(k, Decl(0.js, 21, 3))
57+
>obj.method1 : Symbol(method1, Decl(0.js, 6, 12))
58+
>obj : Symbol(obj, Decl(0.js, 2, 5))
59+
>method1 : Symbol(method1, Decl(0.js, 6, 12))
60+
61+
obj.bar1 = "42";
62+
>obj : Symbol(obj, Decl(0.js, 2, 5))
63+
64+
obj.arrowFunc(0);
65+
>obj.arrowFunc : Symbol(arrowFunc, Decl(0.js, 14, 20))
66+
>obj : Symbol(obj, Decl(0.js, 2, 5))
67+
>arrowFunc : Symbol(arrowFunc, Decl(0.js, 14, 20))
68+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
=== tests/cases/conformance/jsdoc/0.js ===
2+
// @ts-check
3+
var lol = "hello Lol"
4+
>lol : string
5+
>"hello Lol" : "hello Lol"
6+
7+
const obj = {
8+
>obj : { [x: string]: any; foo: string | undefined; bar: string | undefined; method1(arg0: number): number; lol: string; arrowFunc: (arg0: number) => number; }
9+
>{ /** @type {string|undefined} */ foo: undefined, /** @type {string|undefined} */ bar: "42", /** @type {function(number): number} */ method1(n1) { return n1 + 42; }, /** @type {string} */ lol, /** @type {number} */ ['b' + 'ar1']: 42, /** @type {function(number): number} */ arrowFunc: (num) => num + 42} : { [x: string]: any; foo: string | undefined; bar: string | undefined; method1(arg0: number): number; lol: string; arrowFunc: (arg0: number) => number; }
10+
11+
/** @type {string|undefined} */
12+
foo: undefined,
13+
>foo : string | undefined
14+
>undefined : undefined
15+
16+
/** @type {string|undefined} */
17+
bar: "42",
18+
>bar : string | undefined
19+
>"42" : "42"
20+
21+
/** @type {function(number): number} */
22+
method1(n1) {
23+
>method1 : (n1: any) => any
24+
>n1 : any
25+
26+
return n1 + 42;
27+
>n1 + 42 : any
28+
>n1 : any
29+
>42 : 42
30+
31+
},
32+
/** @type {string} */
33+
lol,
34+
>lol : string
35+
36+
/** @type {number} */
37+
['b' + 'ar1']: 42,
38+
>'b' + 'ar1' : string
39+
>'b' : "b"
40+
>'ar1' : "ar1"
41+
>42 : 42
42+
43+
/** @type {function(number): number} */
44+
arrowFunc: (num) => num + 42
45+
>arrowFunc : (arg0: number) => number
46+
>(num) => num + 42 : (num: any) => any
47+
>num : any
48+
>num + 42 : any
49+
>num : any
50+
>42 : 42
51+
}
52+
obj.foo = 'string'
53+
>obj.foo = 'string' : "string"
54+
>obj.foo : string | undefined
55+
>obj : { [x: string]: any; foo: string | undefined; bar: string | undefined; method1(arg0: number): number; lol: string; arrowFunc: (arg0: number) => number; }
56+
>foo : string | undefined
57+
>'string' : "string"
58+
59+
obj.lol
60+
>obj.lol : string
61+
>obj : { [x: string]: any; foo: string | undefined; bar: string | undefined; method1(arg0: number): number; lol: string; arrowFunc: (arg0: number) => number; }
62+
>lol : string
63+
64+
obj.bar = undefined;
65+
>obj.bar = undefined : undefined
66+
>obj.bar : string | undefined
67+
>obj : { [x: string]: any; foo: string | undefined; bar: string | undefined; method1(arg0: number): number; lol: string; arrowFunc: (arg0: number) => number; }
68+
>bar : string | undefined
69+
>undefined : undefined
70+
71+
var k = obj.method1(0);
72+
>k : number
73+
>obj.method1(0) : number
74+
>obj.method1 : (arg0: number) => number
75+
>obj : { [x: string]: any; foo: string | undefined; bar: string | undefined; method1(arg0: number): number; lol: string; arrowFunc: (arg0: number) => number; }
76+
>method1 : (arg0: number) => number
77+
>0 : 0
78+
79+
obj.bar1 = "42";
80+
>obj.bar1 = "42" : "42"
81+
>obj.bar1 : any
82+
>obj : { [x: string]: any; foo: string | undefined; bar: string | undefined; method1(arg0: number): number; lol: string; arrowFunc: (arg0: number) => number; }
83+
>bar1 : any
84+
>"42" : "42"
85+
86+
obj.arrowFunc(0);
87+
>obj.arrowFunc(0) : number
88+
>obj.arrowFunc : (arg0: number) => number
89+
>obj : { [x: string]: any; foo: string | undefined; bar: string | undefined; method1(arg0: number): number; lol: string; arrowFunc: (arg0: number) => number; }
90+
>arrowFunc : (arg0: number) => number
91+
>0 : 0
92+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
tests/cases/conformance/jsdoc/0.js(5,3): error TS2322: Type 'number' is not assignable to type 'string | undefined'.
2+
tests/cases/conformance/jsdoc/0.js(7,3): error TS2322: Type '(n1: any) => string' is not assignable to type '(arg0: number) => number'.
3+
Type 'string' is not assignable to type 'number'.
4+
tests/cases/conformance/jsdoc/0.js(11,3): error TS2322: Type '(n1: any) => string' is not assignable to type '(arg0: number) => number'.
5+
Type 'string' is not assignable to type 'number'.
6+
tests/cases/conformance/jsdoc/0.js(13,3): error TS2322: Type '(num?: string) => string' is not assignable to type '(arg0: number) => number'.
7+
Types of parameters 'num' and 'arg0' are incompatible.
8+
Type 'number' is not assignable to type 'string | undefined'.
9+
tests/cases/conformance/jsdoc/0.js(15,3): error TS2322: Type 'undefined' is not assignable to type 'string'.
10+
tests/cases/conformance/jsdoc/0.js(19,5): error TS2322: Type 'number' is not assignable to type 'string'.
11+
tests/cases/conformance/jsdoc/0.js(22,22): error TS2345: Argument of type '"0"' is not assignable to parameter of type 'number'.
12+
13+
14+
==== tests/cases/conformance/jsdoc/0.js (7 errors) ====
15+
// @ts-check
16+
var lol;
17+
const obj = {
18+
/** @type {string|undefined} */
19+
bar: 42,
20+
~~~~~~~
21+
!!! error TS2322: Type 'number' is not assignable to type 'string | undefined'.
22+
/** @type {function(number): number} */
23+
method1(n1) {
24+
~~~~~~~
25+
!!! error TS2322: Type '(n1: any) => string' is not assignable to type '(arg0: number) => number'.
26+
!!! error TS2322: Type 'string' is not assignable to type 'number'.
27+
return "42";
28+
},
29+
/** @type {function(number): number} */
30+
method2: (n1) => "lol",
31+
~~~~~~~~~~~~~~~~~~~~~~
32+
!!! error TS2322: Type '(n1: any) => string' is not assignable to type '(arg0: number) => number'.
33+
!!! error TS2322: Type 'string' is not assignable to type 'number'.
34+
/** @type {function(number): number} */
35+
arrowFunc: (num="0") => num + 42,
36+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
37+
!!! error TS2322: Type '(num?: string) => string' is not assignable to type '(arg0: number) => number'.
38+
!!! error TS2322: Types of parameters 'num' and 'arg0' are incompatible.
39+
!!! error TS2322: Type 'number' is not assignable to type 'string | undefined'.
40+
/** @type {string} */
41+
lol
42+
~~~
43+
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
44+
}
45+
lol = "string"
46+
/** @type {string} */
47+
var s = obj.method1(0);
48+
~
49+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
50+
51+
/** @type {string} */
52+
var s1 = obj.method2("0");
53+
~~~
54+
!!! error TS2345: Argument of type '"0"' is not assignable to parameter of type 'number'.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//// [0.js]
2+
// @ts-check
3+
var lol;
4+
const obj = {
5+
/** @type {string|undefined} */
6+
bar: 42,
7+
/** @type {function(number): number} */
8+
method1(n1) {
9+
return "42";
10+
},
11+
/** @type {function(number): number} */
12+
method2: (n1) => "lol",
13+
/** @type {function(number): number} */
14+
arrowFunc: (num="0") => num + 42,
15+
/** @type {string} */
16+
lol
17+
}
18+
lol = "string"
19+
/** @type {string} */
20+
var s = obj.method1(0);
21+
22+
/** @type {string} */
23+
var s1 = obj.method2("0");
24+
25+
//// [0.js]
26+
// @ts-check
27+
var lol;
28+
var obj = {
29+
/** @type {string|undefined} */
30+
bar: 42,
31+
/** @type {function(number): number} */
32+
method1: function (n1) {
33+
return "42";
34+
},
35+
/** @type {function(number): number} */
36+
method2: function (n1) { return "lol"; },
37+
/** @type {function(number): number} */
38+
arrowFunc: function (num) {
39+
if (num === void 0) { num = "0"; }
40+
return num + 42;
41+
},
42+
/** @type {string} */
43+
lol: lol
44+
};
45+
lol = "string";
46+
/** @type {string} */
47+
var s = obj.method1(0);
48+
/** @type {string} */
49+
var s1 = obj.method2("0");

0 commit comments

Comments
 (0)