Skip to content

Commit eca3d68

Browse files
authored
@typedef supports nested @property names (#22967)
Previously it did not, because this capability is not documented on usejsdoc.org. However, several people requested this feature.
1 parent 4e76dec commit eca3d68

File tree

4 files changed

+128
-9
lines changed

4 files changed

+128
-9
lines changed

src/compiler/parser.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6497,7 +6497,7 @@ namespace ts {
64976497
const result = target === PropertyLikeParse.Parameter ?
64986498
<JSDocParameterTag>createNode(SyntaxKind.JSDocParameterTag, atToken.pos) :
64996499
<JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos);
6500-
const nestedTypeLiteral = parseNestedTypeLiteral(typeExpression, name);
6500+
const nestedTypeLiteral = parseNestedTypeLiteral(typeExpression, name, target);
65016501
if (nestedTypeLiteral) {
65026502
typeExpression = nestedTypeLiteral;
65036503
isNameFirst = true;
@@ -6511,15 +6511,17 @@ namespace ts {
65116511
return finishNode(result);
65126512
}
65136513

6514-
function parseNestedTypeLiteral(typeExpression: JSDocTypeExpression, name: EntityName) {
6514+
function parseNestedTypeLiteral(typeExpression: JSDocTypeExpression, name: EntityName, target: PropertyLikeParse) {
65156515
if (typeExpression && isObjectOrObjectArrayTypeReference(typeExpression.type)) {
65166516
const typeLiteralExpression = <JSDocTypeExpression>createNode(SyntaxKind.JSDocTypeExpression, scanner.getTokenPos());
6517-
let child: JSDocParameterTag | false;
6517+
let child: JSDocPropertyLikeTag | JSDocTypeTag | false;
65186518
let jsdocTypeLiteral: JSDocTypeLiteral;
65196519
const start = scanner.getStartPos();
6520-
let children: JSDocParameterTag[];
6521-
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.Parameter, name))) {
6522-
children = append(children, child);
6520+
let children: JSDocPropertyLikeTag[];
6521+
while (child = tryParse(() => parseChildParameterOrPropertyTag(target, name))) {
6522+
if (child.kind === SyntaxKind.JSDocParameterTag || child.kind === SyntaxKind.JSDocPropertyTag) {
6523+
children = append(children, child);
6524+
}
65236525
}
65246526
if (children) {
65256527
jsdocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, start);
@@ -6623,7 +6625,7 @@ namespace ts {
66236625
let jsdocTypeLiteral: JSDocTypeLiteral;
66246626
let childTypeTag: JSDocTypeTag;
66256627
const start = scanner.getStartPos();
6626-
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.Property))) {
6628+
while (child = tryParse(() => parseChildPropertyTag())) {
66276629
if (!jsdocTypeLiteral) {
66286630
jsdocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, start);
66296631
}
@@ -6683,8 +6685,10 @@ namespace ts {
66836685
return a.escapedText === b.escapedText;
66846686
}
66856687

6686-
function parseChildParameterOrPropertyTag(target: PropertyLikeParse.Property): JSDocTypeTag | JSDocPropertyTag | false;
6687-
function parseChildParameterOrPropertyTag(target: PropertyLikeParse.Parameter, name: EntityName): JSDocParameterTag | false;
6688+
function parseChildPropertyTag() {
6689+
return parseChildParameterOrPropertyTag(PropertyLikeParse.Property) as JSDocTypeTag | JSDocPropertyTag | false;
6690+
}
6691+
66886692
function parseChildParameterOrPropertyTag(target: PropertyLikeParse, name?: EntityName): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false {
66896693
let canParseTag = true;
66906694
let seenAsterisk = false;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
=== tests/cases/conformance/jsdoc/a.js ===
2+
/** @typedef {Object} App
3+
* @property {string} name
4+
* @property {Object} icons
5+
* @property {string} icons.image32
6+
* @property {string} icons.image64
7+
*/
8+
var ex;
9+
>ex : Symbol(ex, Decl(a.js, 6, 3))
10+
11+
/** @type {App} */
12+
const app = {
13+
>app : Symbol(app, Decl(a.js, 9, 5))
14+
15+
name: 'name',
16+
>name : Symbol(name, Decl(a.js, 9, 13))
17+
18+
icons: {
19+
>icons : Symbol(icons, Decl(a.js, 10, 17))
20+
21+
image32: 'x.png',
22+
>image32 : Symbol(image32, Decl(a.js, 11, 12))
23+
24+
image64: 'y.png',
25+
>image64 : Symbol(image64, Decl(a.js, 12, 25))
26+
}
27+
}
28+
29+
/** @typedef {Object} Opp
30+
* @property {string} name
31+
* @property {Object} oops
32+
* @property {string} horrible
33+
* @type {string} idea
34+
*/
35+
36+
/** @type {Opp} */
37+
var mistake;
38+
>mistake : Symbol(mistake, Decl(a.js, 25, 3))
39+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
=== tests/cases/conformance/jsdoc/a.js ===
2+
/** @typedef {Object} App
3+
* @property {string} name
4+
* @property {Object} icons
5+
* @property {string} icons.image32
6+
* @property {string} icons.image64
7+
*/
8+
var ex;
9+
>ex : any
10+
11+
/** @type {App} */
12+
const app = {
13+
>app : { name: string; icons: { image32: string; image64: string; }; }
14+
>{ name: 'name', icons: { image32: 'x.png', image64: 'y.png', }} : { name: string; icons: { image32: string; image64: string; }; }
15+
16+
name: 'name',
17+
>name : string
18+
>'name' : "name"
19+
20+
icons: {
21+
>icons : { image32: string; image64: string; }
22+
>{ image32: 'x.png', image64: 'y.png', } : { image32: string; image64: string; }
23+
24+
image32: 'x.png',
25+
>image32 : string
26+
>'x.png' : "x.png"
27+
28+
image64: 'y.png',
29+
>image64 : string
30+
>'y.png' : "y.png"
31+
}
32+
}
33+
34+
/** @typedef {Object} Opp
35+
* @property {string} name
36+
* @property {Object} oops
37+
* @property {string} horrible
38+
* @type {string} idea
39+
*/
40+
41+
/** @type {Opp} */
42+
var mistake;
43+
>mistake : { name: string; oops: { horrible: string; }; }
44+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// @noEmit: true
2+
// @allowJs: true
3+
// @checkJs: true
4+
// @strict: true
5+
// @Filename: a.js
6+
7+
/** @typedef {Object} App
8+
* @property {string} name
9+
* @property {Object} icons
10+
* @property {string} icons.image32
11+
* @property {string} icons.image64
12+
*/
13+
var ex;
14+
15+
/** @type {App} */
16+
const app = {
17+
name: 'name',
18+
icons: {
19+
image32: 'x.png',
20+
image64: 'y.png',
21+
}
22+
}
23+
24+
/** @typedef {Object} Opp
25+
* @property {string} name
26+
* @property {Object} oops
27+
* @property {string} horrible
28+
* @type {string} idea
29+
*/
30+
31+
/** @type {Opp} */
32+
var mistake;

0 commit comments

Comments
 (0)