diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index e72ce3ad5aced..ea4b8af183ffa 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -6497,7 +6497,7 @@ namespace ts { const result = target === PropertyLikeParse.Parameter ? createNode(SyntaxKind.JSDocParameterTag, atToken.pos) : createNode(SyntaxKind.JSDocPropertyTag, atToken.pos); - const nestedTypeLiteral = parseNestedTypeLiteral(typeExpression, name); + const nestedTypeLiteral = parseNestedTypeLiteral(typeExpression, name, target); if (nestedTypeLiteral) { typeExpression = nestedTypeLiteral; isNameFirst = true; @@ -6511,15 +6511,17 @@ namespace ts { return finishNode(result); } - function parseNestedTypeLiteral(typeExpression: JSDocTypeExpression, name: EntityName) { + function parseNestedTypeLiteral(typeExpression: JSDocTypeExpression, name: EntityName, target: PropertyLikeParse) { if (typeExpression && isObjectOrObjectArrayTypeReference(typeExpression.type)) { const typeLiteralExpression = createNode(SyntaxKind.JSDocTypeExpression, scanner.getTokenPos()); - let child: JSDocParameterTag | false; + let child: JSDocPropertyLikeTag | JSDocTypeTag | false; let jsdocTypeLiteral: JSDocTypeLiteral; const start = scanner.getStartPos(); - let children: JSDocParameterTag[]; - while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.Parameter, name))) { - children = append(children, child); + let children: JSDocPropertyLikeTag[]; + while (child = tryParse(() => parseChildParameterOrPropertyTag(target, name))) { + if (child.kind === SyntaxKind.JSDocParameterTag || child.kind === SyntaxKind.JSDocPropertyTag) { + children = append(children, child); + } } if (children) { jsdocTypeLiteral = createNode(SyntaxKind.JSDocTypeLiteral, start); @@ -6623,7 +6625,7 @@ namespace ts { let jsdocTypeLiteral: JSDocTypeLiteral; let childTypeTag: JSDocTypeTag; const start = scanner.getStartPos(); - while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.Property))) { + while (child = tryParse(() => parseChildPropertyTag())) { if (!jsdocTypeLiteral) { jsdocTypeLiteral = createNode(SyntaxKind.JSDocTypeLiteral, start); } @@ -6683,8 +6685,10 @@ namespace ts { return a.escapedText === b.escapedText; } - function parseChildParameterOrPropertyTag(target: PropertyLikeParse.Property): JSDocTypeTag | JSDocPropertyTag | false; - function parseChildParameterOrPropertyTag(target: PropertyLikeParse.Parameter, name: EntityName): JSDocParameterTag | false; + function parseChildPropertyTag() { + return parseChildParameterOrPropertyTag(PropertyLikeParse.Property) as JSDocTypeTag | JSDocPropertyTag | false; + } + function parseChildParameterOrPropertyTag(target: PropertyLikeParse, name?: EntityName): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false { let canParseTag = true; let seenAsterisk = false; diff --git a/tests/baselines/reference/typedefTagNested.symbols b/tests/baselines/reference/typedefTagNested.symbols new file mode 100644 index 0000000000000..41ea2faf0b02b --- /dev/null +++ b/tests/baselines/reference/typedefTagNested.symbols @@ -0,0 +1,39 @@ +=== tests/cases/conformance/jsdoc/a.js === +/** @typedef {Object} App + * @property {string} name + * @property {Object} icons + * @property {string} icons.image32 + * @property {string} icons.image64 + */ +var ex; +>ex : Symbol(ex, Decl(a.js, 6, 3)) + +/** @type {App} */ +const app = { +>app : Symbol(app, Decl(a.js, 9, 5)) + + name: 'name', +>name : Symbol(name, Decl(a.js, 9, 13)) + + icons: { +>icons : Symbol(icons, Decl(a.js, 10, 17)) + + image32: 'x.png', +>image32 : Symbol(image32, Decl(a.js, 11, 12)) + + image64: 'y.png', +>image64 : Symbol(image64, Decl(a.js, 12, 25)) + } +} + +/** @typedef {Object} Opp + * @property {string} name + * @property {Object} oops + * @property {string} horrible + * @type {string} idea + */ + +/** @type {Opp} */ +var mistake; +>mistake : Symbol(mistake, Decl(a.js, 25, 3)) + diff --git a/tests/baselines/reference/typedefTagNested.types b/tests/baselines/reference/typedefTagNested.types new file mode 100644 index 0000000000000..6fe8c251205fe --- /dev/null +++ b/tests/baselines/reference/typedefTagNested.types @@ -0,0 +1,44 @@ +=== tests/cases/conformance/jsdoc/a.js === +/** @typedef {Object} App + * @property {string} name + * @property {Object} icons + * @property {string} icons.image32 + * @property {string} icons.image64 + */ +var ex; +>ex : any + +/** @type {App} */ +const app = { +>app : { name: string; icons: { image32: string; image64: string; }; } +>{ name: 'name', icons: { image32: 'x.png', image64: 'y.png', }} : { name: string; icons: { image32: string; image64: string; }; } + + name: 'name', +>name : string +>'name' : "name" + + icons: { +>icons : { image32: string; image64: string; } +>{ image32: 'x.png', image64: 'y.png', } : { image32: string; image64: string; } + + image32: 'x.png', +>image32 : string +>'x.png' : "x.png" + + image64: 'y.png', +>image64 : string +>'y.png' : "y.png" + } +} + +/** @typedef {Object} Opp + * @property {string} name + * @property {Object} oops + * @property {string} horrible + * @type {string} idea + */ + +/** @type {Opp} */ +var mistake; +>mistake : { name: string; oops: { horrible: string; }; } + diff --git a/tests/cases/conformance/jsdoc/typedefTagNested.ts b/tests/cases/conformance/jsdoc/typedefTagNested.ts new file mode 100644 index 0000000000000..a66ff7aa4245f --- /dev/null +++ b/tests/cases/conformance/jsdoc/typedefTagNested.ts @@ -0,0 +1,32 @@ +// @noEmit: true +// @allowJs: true +// @checkJs: true +// @strict: true +// @Filename: a.js + +/** @typedef {Object} App + * @property {string} name + * @property {Object} icons + * @property {string} icons.image32 + * @property {string} icons.image64 + */ +var ex; + +/** @type {App} */ +const app = { + name: 'name', + icons: { + image32: 'x.png', + image64: 'y.png', + } +} + +/** @typedef {Object} Opp + * @property {string} name + * @property {Object} oops + * @property {string} horrible + * @type {string} idea + */ + +/** @type {Opp} */ +var mistake;