From a668ca019b7a28774450019bed7f508261ac2200 Mon Sep 17 00:00:00 2001 From: Eugene Kalinin Date: Sat, 5 Sep 2026 13:38:01 +0300 Subject: [PATCH 1/3] fix(declarations): keep JSDoc typedef comments with their synthesized type Declarations reparsed from a JSDoc `@typedef`/`@callback` take the text range of the tag they came from, which sits inside the comment. The printer finds no leading comment there and the comment is instead swept up by the next statement's leading comment scan, so the type is emitted undocumented while an unrelated declaration gets its docs. Add comment ownership to EmitContext: a claimed comment is emitted by its owner and skipped by every other node scanning across it, and the owner emits nothing but the comment it claimed. The declaration transformer claims a JSDoc comment for the first declaration reparsed out of it when every tag in the comment is one that reparses into a declaration of its own. Fixes #63958 --- tsc/internal/printer/emitcontext.go | 40 +++ tsc/internal/printer/printer.go | 28 +- .../transformers/declarations/transform.go | 53 ++++ .../compiler/exportAssignmentMerging5.js | 6 +- .../compiler/exportAssignmentMerging6.js | 6 +- .../jsDeclarationEmitDoesNotRenameImport.js | 6 +- ...ssignedFunctionWithExtraTypedefsMembers.js | 8 +- .../compiler/jsDeclarationsInheritedTypes.js | 6 +- .../compiler/jsDocCallbackExport1.js | 2 +- .../compiler/jsDocCallbackExport2.js | 2 +- ...jsTypedefMergedWithModuleExportProperty.js | 4 +- .../reference/compiler/jsdocMultilineUnion.js | 2 +- .../jsdocNonIdentifierPropertiesAndParams.js | 2 +- ...ationImportTypeInGlobalThisTypeArgument.js | 2 +- .../reference/compiler/typedefHoisting.js | 4 +- .../conformance/callbackOnConstructor.js | 10 +- .../conformance/checkJsdocSatisfiesTag15.js | 2 +- ...sDeclarationsClassStatic(target=es2015).js | 10 +- .../jsDeclarationsDefault(target=es2015).js | 4 +- ...sDeclarationsDefaultsErr(target=es2015).js | 6 +- ...assesCjsExportAssignment(target=es2015).js | 20 +- ...sFunctionPrototypeStatic(target=es2015).js | 2 +- ...ationsImportAliasExposedWithinNamespace.js | 16 +- ...onsImportAliasExposedWithinNamespaceCjs.js | 9 +- .../jsDeclarationsImportNamespacedType.js | 2 +- ...tionsParameterTagReusesInputNodeInEmit1.js | 4 +- .../conformance/jsDeclarationsTypeAliases.js | 18 +- .../jsDeclarationsTypedefAndImportTypes.js | 8 +- .../jsDeclarationsTypedefCommentPlacement.js | 241 ++++++++++++++++++ ...eclarationsTypedefCommentPlacement.symbols | 121 +++++++++ ...sDeclarationsTypedefCommentPlacement.types | 123 +++++++++ ...eclarationsTypedefDescriptionsPreserved.js | 16 +- ...tionsTypedefPropertyAndExportAssignment.js | 24 +- .../jsDeclarationsUniqueSymbolUsage.js | 6 +- .../reference/conformance/linkTagEmit1.js | 4 +- .../conformance/recursiveTypeReferences2.js | 4 +- .../conformance/templateInsideCallback.js | 17 +- .../typedefModuleExportsIndirect1.js | 4 +- .../typedefModuleExportsIndirect2.js | 4 +- .../typedefModuleExportsIndirect3.js | 4 +- .../typedefOnSemicolonClassElement.js | 1 + .../conformance/typedefOnStatements.js | 17 ++ ...based-projects-and-emits-them-correctly.js | 14 +- .../jsDeclarationsTypedefCommentPlacement.ts | 100 ++++++++ 44 files changed, 858 insertions(+), 124 deletions(-) create mode 100644 tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.js create mode 100644 tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.symbols create mode 100644 tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.types create mode 100644 tsc/testdata/tests/cases/conformance/jsdoc/declarations/jsDeclarationsTypedefCommentPlacement.ts diff --git a/tsc/internal/printer/emitcontext.go b/tsc/internal/printer/emitcontext.go index ee483873f0d58..8a0405470065d 100644 --- a/tsc/internal/printer/emitcontext.go +++ b/tsc/internal/printer/emitcontext.go @@ -25,6 +25,8 @@ type EmitContext struct { varScopeStack core.Stack[*varScope] letScopeStack core.Stack[*varScope] emitHelpers collections.OrderedSet[*EmitHelper] + // Comments owned by a node they do not textually precede, keyed by the comment's start position. + claimedComments map[int]*ast.Node } type environmentFlags int @@ -523,6 +525,7 @@ type emitNodeFlags uint32 const ( hasCommentRange emitNodeFlags = 1 << iota hasSourceMapRange + claimsComment ) type SnippetKind int @@ -619,6 +622,43 @@ func (c *EmitContext) AssignCommentRange(to *ast.Node, from *ast.Node) { c.SetCommentRange(to, c.CommentRange(from)) } +// Gives the comment at loc to node, which need not be the node the comment textually precedes. A +// claimed comment is emitted by its owner and skipped by every other node whose leading comment scan +// runs across it, and the owner in turn emits no leading comment other than the one it claimed. This +// lets a declaration reparsed out of a JSDoc tag keep the comment that declared it instead of +// leaving it in front of the following statement. scanFrom is where the printer starts looking for +// leading comments and must sit before the line the comment starts on, since comment scans only +// collect after a line break. The first claim of a comment wins. +func (c *EmitContext) ClaimComment(node *ast.Node, scanFrom int, loc core.TextRange) { + if _, claimed := c.claimedComments[loc.Pos()]; claimed { + return + } + if c.claimedComments == nil { + c.claimedComments = make(map[int]*ast.Node) + } + c.claimedComments[loc.Pos()] = node + c.SetCommentRange(node, core.NewTextRange(scanFrom, loc.End())) + c.emitNodes.Get(node).flags |= claimsComment +} + +// Reports whether node was given a comment to emit by ClaimComment. +func (c *EmitContext) ClaimsComment(node *ast.Node) bool { + if node == nil { + return false + } + emitNode := c.emitNodes.TryGet(node) + return emitNode != nil && emitNode.flags&claimsComment != 0 +} + +// Reports whether node emits the comment starting at pos as one of its leading comments. node is nil +// when comments are emitted outside of any node. +func (c *EmitContext) EmitsLeadingComment(node *ast.Node, pos int) bool { + if owner, claimed := c.claimedComments[pos]; claimed { + return owner == node + } + return !c.ClaimsComment(node) +} + // Gets the range to use for a node when emitting source maps. func (c *EmitContext) SourceMapRange(node *ast.Node) core.TextRange { if emitNode := c.emitNodes.TryGet(node); emitNode != nil && emitNode.flags&hasSourceMapRange != 0 { diff --git a/tsc/internal/printer/printer.go b/tsc/internal/printer/printer.go index 31547cde51d64..901dfd27e17b9 100644 --- a/tsc/internal/printer/printer.go +++ b/tsc/internal/printer/printer.go @@ -5435,7 +5435,7 @@ func (p *Printer) emitLeadingCommentsOfNode(node *ast.Node, emitFlags EmitFlags, // Emit leading comments if the position is not synthesized and the node // has not opted out from emitting leading comments. if !skipLeadingComments { - p.emitLeadingComments(pos, node.Kind == ast.KindNotEmittedStatement /*elided*/) + p.emitLeadingCommentsWorker(pos, node, node.Kind == ast.KindNotEmittedStatement /*elided*/) } if !skipLeadingComments || (pos >= 0 && (emitFlags&EFNoLeadingComments) != 0) { @@ -5533,6 +5533,12 @@ func (p *Printer) writeSynthesizedComment(comment SynthesizedComment) { } func (p *Printer) emitLeadingComments(pos int, elided bool) bool { + return p.emitLeadingCommentsWorker(pos, nil /*node*/, elided) +} + +// node decides ownership of comments claimed by way of EmitContext.ClaimComment, and is nil when +// comments are emitted outside of any node. +func (p *Printer) emitLeadingCommentsWorker(pos int, node *ast.Node, elided bool) bool { // Emit the leading comments only if the container's pos doesn't match because the container should take care of emitting these comments if p.commentsDisabled || p.currentSourceFile == nil || ast.PositionIsSynthesized(pos) || pos == p.containerPos { return false @@ -5557,8 +5563,11 @@ func (p *Printer) emitLeadingComments(pos int, elided bool) bool { return false } - // skip detached comments - if p.detachedCommentsInfo.Len() > 0 { + // skip detached comments - a node claiming a comment has to keep scanning across them, since the + // comment it claims may be one the detached pass left behind for it, and the node the comments + // were detached from still needs to consume them + claimsComment := p.emitContext.ClaimsComment(node) + if p.detachedCommentsInfo.Len() > 0 && !claimsComment { if info := p.detachedCommentsInfo.Peek(); info.nodePos == pos { pos = p.detachedCommentsInfo.Pop().detachedCommentEndPos } @@ -5566,6 +5575,9 @@ func (p *Printer) emitLeadingComments(pos int, elided bool) bool { var comments []ast.CommentRange for comment := range scanner.GetLeadingCommentRanges(p.emitContext.Factory.AsNodeFactory(), p.currentSourceFile.Text(), pos) { + if !p.emitContext.EmitsLeadingComment(node, comment.Pos()) { + continue + } if p.shouldWriteComment(comment) && p.shouldEmitCommentIfTripleSlash(comment, tripleSlash) { comments = append(comments, comment) } @@ -5576,7 +5588,13 @@ func (p *Printer) emitLeadingComments(pos int, elided bool) bool { } // Leading comments are emitted as /*leading comment1*/space/*leading comment*/space - return p.emitComments(comments, commentSeparatorAfter) + hasWrittenComment := p.emitComments(comments, commentSeparatorAfter) + if hasWrittenComment && claimsComment && !p.writer.IsAtStartOfLine() { + // A claimed comment no longer precedes the text it did in the source, so it cannot borrow + // that text's line break. + p.writeLine() + } + return hasWrittenComment } func (p *Printer) shouldEmitCommentIfTripleSlash(comment ast.CommentRange, tripleSlash core.Tristate) bool { @@ -5738,7 +5756,7 @@ func (p *Printer) emitDetachedComments(textRange core.TextRange) (result detache // Filter to only comments that should be written (e.g., JSDoc-style in declaration emit) var commentsToEmit []ast.CommentRange for _, comment := range detachedComments { - if p.shouldWriteComment(comment) { + if p.shouldWriteComment(comment) && p.emitContext.EmitsLeadingComment(nil /*node*/, comment.Pos()) { commentsToEmit = append(commentsToEmit, comment) } } diff --git a/tsc/internal/transformers/declarations/transform.go b/tsc/internal/transformers/declarations/transform.go index 32fa0201a9ef5..857a999bb35fa 100644 --- a/tsc/internal/transformers/declarations/transform.go +++ b/tsc/internal/transformers/declarations/transform.go @@ -458,9 +458,62 @@ func (tx *DeclarationTransformer) transformAndReplaceLatePaintedStatements(state } } + tx.claimReparsedJSDocComments(results) return tx.Factory().NewNodeList(results) } +// Tags that reparse into a declaration of their own rather than contributing to the node the +// containing JSDoc comment is attached to. +func isUnhostedJSDocTag(tag *ast.Node) bool { + switch tag.Kind { + case ast.KindJSDocTypedefTag, ast.KindJSDocCallbackTag, ast.KindJSDocImportTag, ast.KindJSDocOverloadTag: + return true + } + return false +} + +// A JSDoc comment whose every tag is unhosted documents the declarations reparsed out of it, not the +// statement it is attached to. +func documentsOnlyReparsedDeclarations(jsdoc *ast.Node) bool { + tags := jsdoc.AsJSDoc().Tags + return tags != nil && len(tags.Nodes) > 0 && core.Every(tags.Nodes, isUnhostedJSDocTag) +} + +// A JSDoc node starts at the full start of the node it documents rather than at its own `/**`, so +// recover the range of the comment itself. +func (tx *DeclarationTransformer) commentRangeOfJSDoc(jsdoc *ast.Node, file *ast.SourceFile) (core.TextRange, bool) { + for comment := range scanner.GetLeadingCommentRanges(tx.EmitContext().Factory.AsNodeFactory(), file.Text(), jsdoc.Pos()) { + if comment.End() == jsdoc.End() { + return comment.TextRange, true + } + } + return core.TextRange{}, false +} + +// Declarations reparsed from JSDoc (`@typedef`, `@callback`, `@import`, `@overload`) take the text +// range of the tag they came from, which sits inside the comment. The printer therefore finds no +// leading comment for them and hands the comment to the following statement instead, documenting +// the wrong declaration. Claim each such comment for the first declaration reparsed out of it. +func (tx *DeclarationTransformer) claimReparsedJSDocComments(statements []*ast.Node) { + file := tx.state.currentSourceFile + if file == nil || !ast.IsSourceFileJS(file) { + return + } + for _, statement := range statements { + original := tx.EmitContext().MostOriginal(statement) + if original.Flags&ast.NodeFlagsReparsed == 0 { + continue + } + jsdoc := core.FirstOrNil(original.EagerJSDoc(file)) + if jsdoc == nil || !documentsOnlyReparsedDeclarations(jsdoc) { + continue + } + if loc, ok := tx.commentRangeOfJSDoc(jsdoc, file); ok { + tx.EmitContext().ClaimComment(statement, jsdoc.Pos(), loc) + } + } +} + func (tx *DeclarationTransformer) getReferencedFiles(outputFilePath string) (results []*ast.FileReference) { // Handle path rewrites for triple slash ref comments for _, pair := range tx.rawReferencedFiles { diff --git a/tsc/testdata/baselines/reference/compiler/exportAssignmentMerging5.js b/tsc/testdata/baselines/reference/compiler/exportAssignmentMerging5.js index 4f0f0afc8a7d3..fe4d7d1c7db0e 100644 --- a/tsc/testdata/baselines/reference/compiler/exportAssignmentMerging5.js +++ b/tsc/testdata/baselines/reference/compiler/exportAssignmentMerging5.js @@ -29,14 +29,14 @@ let v1 = { x: "test" }; //// [a.d.ts] -/** - * @typedef {{x: string}} Foo - */ declare const _exports: { a: number; b: string; }; export = _exports; +/** + * @typedef {{x: string}} Foo + */ export type Foo = { x: string; }; diff --git a/tsc/testdata/baselines/reference/compiler/exportAssignmentMerging6.js b/tsc/testdata/baselines/reference/compiler/exportAssignmentMerging6.js index 40f8911c5ffc4..fedf752ed885c 100644 --- a/tsc/testdata/baselines/reference/compiler/exportAssignmentMerging6.js +++ b/tsc/testdata/baselines/reference/compiler/exportAssignmentMerging6.js @@ -33,12 +33,12 @@ let v1 = { x: "test" }; //// [a.d.ts] -export type Foo = { - x: string; -}; /** * @typedef {{x: string}} Foo */ +export type Foo = { + x: string; +}; export declare const x = 1; //// [b.d.ts] export {}; diff --git a/tsc/testdata/baselines/reference/compiler/jsDeclarationEmitDoesNotRenameImport.js b/tsc/testdata/baselines/reference/compiler/jsDeclarationEmitDoesNotRenameImport.js index 5f0cf191f367c..caaf932650352 100644 --- a/tsc/testdata/baselines/reference/compiler/jsDeclarationEmitDoesNotRenameImport.js +++ b/tsc/testdata/baselines/reference/compiler/jsDeclarationEmitDoesNotRenameImport.js @@ -45,13 +45,13 @@ declare class Test { export default Test; //// [index.d.ts] import Test from './test/Test.js'; -export type Options = { - test?: typeof import("./Test.js").default; -}; /** * @typedef {Object} Options * @property {typeof import("./Test.js").default} [test] */ +export type Options = { + test?: typeof import("./Test.js").default; +}; declare class X extends Test { test: import("./Test.js").default | undefined; /** diff --git a/tsc/testdata/baselines/reference/compiler/jsDeclarationEmitExportAssignedFunctionWithExtraTypedefsMembers.js b/tsc/testdata/baselines/reference/compiler/jsDeclarationEmitExportAssignedFunctionWithExtraTypedefsMembers.js index 10157988ac1cf..6c991faf611f4 100644 --- a/tsc/testdata/baselines/reference/compiler/jsDeclarationEmitExportAssignedFunctionWithExtraTypedefsMembers.js +++ b/tsc/testdata/baselines/reference/compiler/jsDeclarationEmitExportAssignedFunctionWithExtraTypedefsMembers.js @@ -25,15 +25,15 @@ module.exports = function loader(options) { }; //// [index.d.ts] -/** - * @typedef Options - * @property {string} opt - */ export = loader; /** * @param {Options} options */ declare function loader(options: Options): void; +/** + * @typedef Options + * @property {string} opt + */ export type Options = { opt: string; }; diff --git a/tsc/testdata/baselines/reference/compiler/jsDeclarationsInheritedTypes.js b/tsc/testdata/baselines/reference/compiler/jsDeclarationsInheritedTypes.js index 94d864452f5c6..31ad8e03dcd11 100644 --- a/tsc/testdata/baselines/reference/compiler/jsDeclarationsInheritedTypes.js +++ b/tsc/testdata/baselines/reference/compiler/jsDeclarationsInheritedTypes.js @@ -43,13 +43,13 @@ class C3 extends C1 { type A = { a: string; }; -type B = { - b: number; -}; /** * @typedef B * @property {number} b */ +type B = { + b: number; +}; declare class C1 { /** * @type {A} diff --git a/tsc/testdata/baselines/reference/compiler/jsDocCallbackExport1.js b/tsc/testdata/baselines/reference/compiler/jsDocCallbackExport1.js index eac66bbf52af0..82b29e1ee3741 100644 --- a/tsc/testdata/baselines/reference/compiler/jsDocCallbackExport1.js +++ b/tsc/testdata/baselines/reference/compiler/jsDocCallbackExport1.js @@ -12,10 +12,10 @@ function f1() {} //// [x.d.ts] -type Foo = (x: string) => number; /** * @callback Foo * @param {string} x * @returns {number} */ +type Foo = (x: string) => number; declare function f1(): void; diff --git a/tsc/testdata/baselines/reference/compiler/jsDocCallbackExport2.js b/tsc/testdata/baselines/reference/compiler/jsDocCallbackExport2.js index 8a894ce57d28e..b3048ca6f49ea 100644 --- a/tsc/testdata/baselines/reference/compiler/jsDocCallbackExport2.js +++ b/tsc/testdata/baselines/reference/compiler/jsDocCallbackExport2.js @@ -12,10 +12,10 @@ export function f1() {} //// [x.d.ts] -export type Foo = (x: string) => number; /** * @callback Foo * @param {string} x * @returns {number} */ +export type Foo = (x: string) => number; export declare function f1(): void; diff --git a/tsc/testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.js b/tsc/testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.js index 9708ba8ee6f56..ebb6528f12645 100644 --- a/tsc/testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.js +++ b/tsc/testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.js @@ -36,13 +36,13 @@ declare namespace ModuleGraphConnection { const _exported: typeof T; export { _exported as T }; } -export type T = typeof T; /** @typedef {typeof T} T */ +export type T = typeof T; declare const T: unique symbol; //// [repro.d.ts] export = Repro; -/** @typedef {import('./local-lib/ModuleGraphConnection')} ImportedType */ /** @type {ImportedType} */ declare class Repro { } +/** @typedef {import('./local-lib/ModuleGraphConnection')} ImportedType */ export type ImportedType = import('./local-lib/ModuleGraphConnection'); diff --git a/tsc/testdata/baselines/reference/compiler/jsdocMultilineUnion.js b/tsc/testdata/baselines/reference/compiler/jsdocMultilineUnion.js index 8aad70d67947f..cdb8bf288f6a0 100644 --- a/tsc/testdata/baselines/reference/compiler/jsdocMultilineUnion.js +++ b/tsc/testdata/baselines/reference/compiler/jsdocMultilineUnion.js @@ -14,7 +14,6 @@ //// [a.d.ts] -type T = ("a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n")[]; /** * @typedef {("a"|"b"|"c"| * "d"|"e"|"f"|"g"| @@ -23,3 +22,4 @@ type T = ("a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" * "k"|"l"| * "m"|"n")[]} T */ +type T = ("a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n")[]; diff --git a/tsc/testdata/baselines/reference/compiler/jsdocNonIdentifierPropertiesAndParams.js b/tsc/testdata/baselines/reference/compiler/jsdocNonIdentifierPropertiesAndParams.js index c1b67a05e60bd..30f5a58ec4998 100644 --- a/tsc/testdata/baselines/reference/compiler/jsdocNonIdentifierPropertiesAndParams.js +++ b/tsc/testdata/baselines/reference/compiler/jsdocNonIdentifierPropertiesAndParams.js @@ -51,9 +51,9 @@ export type ButtonProps = { * @returns {ButtonProps} */ export declare function Button(props: ButtonProps): ButtonProps; -export type ButtonPropsCallback = (props_like?: ButtonProps) => ButtonProps; /** * @callback ButtonPropsCallback * @param {ButtonProps} [props-like] * @returns {ButtonProps} */ +export type ButtonPropsCallback = (props_like?: ButtonProps) => ButtonProps; diff --git a/tsc/testdata/baselines/reference/compiler/reuseTypeAnnotationImportTypeInGlobalThisTypeArgument.js b/tsc/testdata/baselines/reference/compiler/reuseTypeAnnotationImportTypeInGlobalThisTypeArgument.js index 41f5b45f14933..a07ff1306075c 100644 --- a/tsc/testdata/baselines/reference/compiler/reuseTypeAnnotationImportTypeInGlobalThisTypeArgument.js +++ b/tsc/testdata/baselines/reference/compiler/reuseTypeAnnotationImportTypeInGlobalThisTypeArgument.js @@ -34,11 +34,11 @@ export const blah = handleParamGovernance({}); //// [types.d.ts] export {}; -export type ParamStateRecord = Record; /** * @typedef {Record} ParamStateRecord a Record containing * keyword pairs with descriptions of parameters under governance. */ +export type ParamStateRecord = Record; //// [index.d.ts] export declare const blah: { publicMixin: { diff --git a/tsc/testdata/baselines/reference/compiler/typedefHoisting.js b/tsc/testdata/baselines/reference/compiler/typedefHoisting.js index 6803d6b512432..2604de95a15b1 100644 --- a/tsc/testdata/baselines/reference/compiler/typedefHoisting.js +++ b/tsc/testdata/baselines/reference/compiler/typedefHoisting.js @@ -19,15 +19,15 @@ export {} //// [y.d.ts] -export type Bar = string; /** @typedef {string} Bar */ +export type Bar = string; export {}; //// [x.d.ts] import type { Bar } from "./y"; +/** @typedef {Bar[]} Bars */ type Bars = Bar[]; declare class C { /** @import {Bar} from "./y" */ - /** @typedef {Bar[]} Bars */ /** @type {Bars} */ foo: Bars; bar(/** @type {Bar} */ x: Bar): string; diff --git a/tsc/testdata/baselines/reference/conformance/callbackOnConstructor.js b/tsc/testdata/baselines/reference/conformance/callbackOnConstructor.js index 4c9906e3cd709..29251540bfb0a 100644 --- a/tsc/testdata/baselines/reference/conformance/callbackOnConstructor.js +++ b/tsc/testdata/baselines/reference/conformance/callbackOnConstructor.js @@ -32,13 +32,13 @@ var ooscope2 = s => s.length > 0; //// [callbackOnConstructor.d.ts] +/** + * @callback ValueGetter_2 + * @param {string} name + * @returns {boolean|number|string|undefined} + */ export type ValueGetter_2 = (name: string) => boolean | number | string | undefined; export declare class Preferences { assignability: string; - /** - * @callback ValueGetter_2 - * @param {string} name - * @returns {boolean|number|string|undefined} - */ constructor(); } diff --git a/tsc/testdata/baselines/reference/conformance/checkJsdocSatisfiesTag15.js b/tsc/testdata/baselines/reference/conformance/checkJsdocSatisfiesTag15.js index e53c5236eb5a7..aaa77030248ac 100644 --- a/tsc/testdata/baselines/reference/conformance/checkJsdocSatisfiesTag15.js +++ b/tsc/testdata/baselines/reference/conformance/checkJsdocSatisfiesTag15.js @@ -85,8 +85,8 @@ export function fn7(uuid) { } //// [a.d.ts] /** @satisfies {(uuid: string) => void} */ export declare const fn1: (uuid: string) => void; -export type Foo = Parameters; /** @typedef {Parameters} Foo */ +export type Foo = Parameters; /** @type Foo */ export declare const v1: Foo; /** @type Foo */ diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsClassStatic(target=es2015).js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsClassStatic(target=es2015).js index f71f4d003992d..5d5e8f3dc0973 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsClassStatic(target=es2015).js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsClassStatic(target=es2015).js @@ -62,14 +62,14 @@ declare const Strings: { a: string; b: string; }; +/** + * @typedef {Object} HandlerOptions + * @property {String} name + * Should be able to export a type alias at the same time. + */ export type HandlerOptions = { /** * Should be able to export a type alias at the same time. */ name: string; }; -/** - * @typedef {Object} HandlerOptions - * @property {String} name - * Should be able to export a type alias at the same time. - */ diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsDefault(target=es2015).js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsDefault(target=es2015).js index e875e57bd88e1..d5eb67a582caa 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsDefault(target=es2015).js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsDefault(target=es2015).js @@ -122,13 +122,13 @@ export default Bar; //// [index5.d.ts] declare const _default = 12; export default _default; -export type default = string | number; /** * @typedef {string | number} default */ +export type default = string | number; //// [index6.d.ts] export default function func(): void; -export type default = string | number; /** * @typedef {string | number} default */ +export type default = string | number; diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsDefaultsErr(target=es2015).js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsDefaultsErr(target=es2015).js index 062db66cf59ab..3cb662fdade65 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsDefaultsErr(target=es2015).js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsDefaultsErr(target=es2015).js @@ -70,21 +70,21 @@ declare class Cls { static y: string; } export default Cls; -export type default = string | number; /** * @typedef {string | number} default */ +export type default = string | number; //// [index2.d.ts] export default class C { } -export type default = string | number; /** * @typedef {string | number} default */ +export type default = string | number; //// [index3.d.ts] declare const x = 12; export { x as default }; -export type default = string | number; /** * @typedef {string | number} default */ +export type default = string | number; diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsFunctionClassesCjsExportAssignment(target=es2015).js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsFunctionClassesCjsExportAssignment(target=es2015).js index eaaac056b90b7..5d75d478e31b6 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsFunctionClassesCjsExportAssignment(target=es2015).js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsFunctionClassesCjsExportAssignment(target=es2015).js @@ -147,6 +147,7 @@ export = Timer; */ declare function Timer(timeout: number): void; //// [context.d.ts] +export = Context; /** * Imports * @@ -154,18 +155,9 @@ declare function Timer(timeout: number): void; * @typedef {import("./hook")} Hook * @typedef {import("./hook").HookHandler} HookHandler */ -export = Context; export type Timer = import("./timer"); export type Hook = import("./hook"); export type HookHandler = import("./hook").HookHandler; -export type Input = { - timer: Timer; - hook: Hook; -}; -export type State = { - timer: Timer; - hook: Hook; -}; /** * Input type definition * @@ -173,6 +165,10 @@ export type State = { * @prop {Timer} timer * @prop {Hook} hook */ +export type Input = { + timer: Timer; + hook: Hook; +}; /** * State type definition * @@ -180,6 +176,10 @@ export type State = { * @prop {Timer} timer * @prop {Hook} hook */ +export type State = { + timer: Timer; + hook: Hook; +}; /** * New `Context` * @@ -199,10 +199,10 @@ declare namespace Context { } //// [hook.d.ts] export = Hook; -export type HookHandler = (arg: import("./context")) => void; /** * @typedef {(arg: import("./context")) => void} HookHandler */ +export type HookHandler = (arg: import("./context")) => void; /** * @param {HookHandler} handle */ diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsFunctionPrototypeStatic(target=es2015).js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsFunctionPrototypeStatic(target=es2015).js index 81f31ee4100a9..465221d1fe410 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsFunctionPrototypeStatic(target=es2015).js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsFunctionPrototypeStatic(target=es2015).js @@ -37,10 +37,10 @@ declare namespace MyClass { var staticMethod: () => void; var staticProperty: number; } -export type DoneCB = (failures: number) => any; /** * Callback to be invoked when test execution is complete. * * @callback DoneCB * @param {number} failures - Number of failures that occurred. */ +export type DoneCB = (failures: number) => any; diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsImportAliasExposedWithinNamespace.js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsImportAliasExposedWithinNamespace.js index 4d58c2fd590c8..60efc5acc5c2d 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsImportAliasExposedWithinNamespace.js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsImportAliasExposedWithinNamespace.js @@ -61,9 +61,15 @@ export {testFn, testFnTypes}; */ declare const myTypes: Record; export declare namespace myTypes { + /** @typedef {string|RegExp|Array} myTypes.typeA */ export type typeA = string | RegExp | Array; } export declare namespace myTypes { + /** + * @typedef myTypes.typeB + * @property {myTypes.typeA} prop1 - Prop 1. + * @property {string} prop2 - Prop 2. + */ export type typeB = { /** * - Prop 1. @@ -76,15 +82,9 @@ export declare namespace myTypes { }; } export declare namespace myTypes { + /** @typedef {myTypes.typeB|Function} myTypes.typeC */ export type typeC = myTypes.typeB | Function; } -/** @typedef {string|RegExp|Array} myTypes.typeA */ -/** - * @typedef myTypes.typeB - * @property {myTypes.typeA} prop1 - Prop 1. - * @property {string} prop2 - Prop 2. - */ -/** @typedef {myTypes.typeB|Function} myTypes.typeC */ export { myTypes }; //// [file2.d.ts] import { myTypes } from './file.js'; @@ -95,9 +95,9 @@ import { myTypes } from './file.js'; */ declare const testFnTypes: Record; export declare namespace testFnTypes { + /** @typedef {boolean|myTypes.typeC} testFnTypes.input */ export type input = boolean | myTypes.typeC; } -/** @typedef {boolean|myTypes.typeC} testFnTypes.input */ /** * @function testFn * @description A test function. diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsImportAliasExposedWithinNamespaceCjs.js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsImportAliasExposedWithinNamespaceCjs.js index 1899566abe4ff..b63dd96e5104b 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsImportAliasExposedWithinNamespaceCjs.js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsImportAliasExposedWithinNamespaceCjs.js @@ -62,9 +62,15 @@ export { myTypes }; */ declare const myTypes: Record; export declare namespace myTypes { + /** @typedef {string|RegExp|Array} myTypes.typeA */ export type typeA = string | RegExp | Array; } export declare namespace myTypes { + /** + * @typedef myTypes.typeB + * @property {myTypes.typeA} prop1 - Prop 1. + * @property {string} prop2 - Prop 2. + */ export type typeB = { /** * - Prop 1. @@ -77,6 +83,7 @@ export declare namespace myTypes { }; } export declare namespace myTypes { + /** @typedef {myTypes.typeB|Function} myTypes.typeC */ export type typeC = myTypes.typeB | Function; } //// [file2.d.ts] @@ -93,9 +100,9 @@ import { myTypes } from './file.js'; */ declare const testFnTypes: Record; export declare namespace testFnTypes { + /** @typedef {boolean|myTypes.typeC} testFnTypes.input */ export type input = boolean | myTypes.typeC; } -/** @typedef {boolean|myTypes.typeC} testFnTypes.input */ /** * @function testFn * @description A test function. diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsImportNamespacedType.js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsImportNamespacedType.js index 94d7aaee6e878..ffe3506ee605c 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsImportNamespacedType.js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsImportNamespacedType.js @@ -14,9 +14,9 @@ export var dummy = 1 //// [mod1.d.ts] export declare namespace Dotted { + /** @typedef {number} Dotted.Name */ export type Name = number; } -/** @typedef {number} Dotted.Name */ export declare var dummy: number; //// [file.d.ts] export {}; diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsParameterTagReusesInputNodeInEmit1.js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsParameterTagReusesInputNodeInEmit1.js index 61c74727f7018..874b247c70887 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsParameterTagReusesInputNodeInEmit1.js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsParameterTagReusesInputNodeInEmit1.js @@ -71,13 +71,13 @@ declare namespace BaseFactory { export { Base }; } //// [file.d.ts] -type BaseFactory = import('./base'); -type BaseFactoryFactory = (factory: import('./base')) => any; /** @typedef {import('./base')} BaseFactory */ +type BaseFactory = import('./base'); /** * @callback BaseFactoryFactory * @param {import('./base')} factory */ +type BaseFactoryFactory = (factory: import('./base')) => any; /** @enum {import('./base')} */ declare const couldntThinkOfAny: {}; /** diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypeAliases.js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypeAliases.js index 174bb02fd7dbd..5a06d75a5e466 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypeAliases.js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypeAliases.js @@ -106,15 +106,10 @@ class LocalThing { //// [index.d.ts] export {}; -export type PropName = string | number | symbol; -export type NumberToStringCb = (a: number) => string; -export type MixinName = T & { - name: string; -}; -export type Identity = (x: T) => T; /** * @typedef {string | number | symbol} PropName */ +export type PropName = string | number | symbol; /** * Callback * @@ -122,6 +117,11 @@ export type Identity = (x: T) => T; * @param {number} a * @returns {string} */ +export type NumberToStringCb = (a: number) => string; +export type MixinName = T & { + name: string; +}; +export type Identity = (x: T) => T; /** * @template T * @typedef {T & {name: string}} MixinName @@ -140,12 +140,12 @@ declare const _exports: { ExportedThing: typeof ExportedThing; }; export = _exports; -export type SomeType = { - x: string; -} | number | LocalThing | ExportedThing; /** * @typedef {{x: string} | number | LocalThing | ExportedThing} SomeType */ +export type SomeType = { + x: string; +} | number | LocalThing | ExportedThing; /** * @param {number} x * @returns {SomeType} diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefAndImportTypes.js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefAndImportTypes.js index 11faffb1309d6..2b81c60a347a9 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefAndImportTypes.js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefAndImportTypes.js @@ -67,10 +67,10 @@ module.exports = { //// [conn.d.ts] +export = Conn; /** * @typedef {string | number} Whatever */ -export = Conn; export type Whatever = string | number; declare class Conn { constructor(); @@ -78,13 +78,13 @@ declare class Conn { method(): void; } //// [usage.d.ts] -/** - * @typedef {import("./conn")} Conn - */ declare const _exports: { Wrap: typeof Wrap; }; export = _exports; +/** + * @typedef {import("./conn")} Conn + */ export type Conn = import("./conn"); declare class Wrap { connItem: number; diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.js new file mode 100644 index 0000000000000..f06ba8f26595c --- /dev/null +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.js @@ -0,0 +1,241 @@ +//// [tests/cases/conformance/jsdoc/declarations/jsDeclarationsTypedefCommentPlacement.ts] //// + +//// [typedef.js] +export function noop() {} + +/** + * A point in 2D space. + * @typedef {Object} Point + * @property {number} x + * @property {number} y + */ + +/** + * @param {Point} p + */ +export function dist(p) { + return p.x; +} + +//// [callback.js] +export function noop() {} + +/** + * Compares two numbers. + * @callback Comparator + * @param {number} a + * @param {number} b + * @returns {number} + */ + +/** + * @param {number[]} arr + * @param {Comparator} cmp + */ +export function sortWith(arr, cmp) { + return arr.slice().sort(cmp); +} + +//// [shared.js] +export function noop() {} + +/** + * Two aliases from one comment. + * @typedef {number} A + * @typedef {string} B + */ + +export function f() {} + +//// [hosted.js] +export function noop() {} + +/** + * Documents g, not N. + * @typedef {number} N + * @param {N} x + */ +export function g(x) { + return x; +} + +//// [trailing.js] +export function noop() {} + +/** + * Nothing follows this one. + * @typedef {Object} Trailing + * @property {number} x + */ + +//// [imported.js] +export function noop() {} + +/** + * Imports and defines in one comment. + * @import {Point} from "./typedef.js" + * @typedef {Point[]} Path + */ + +export function h() {} + +//// [preceded.js] +export function noop() {} + +// not a JSDoc comment +/** + * @typedef {number} Q + */ + +export function i() {} + + +//// [typedef.js] +export function noop() { } +/** + * A point in 2D space. + * @typedef {Object} Point + * @property {number} x + * @property {number} y + */ +/** + * @param {Point} p + */ +export function dist(p) { + return p.x; +} +//// [callback.js] +export function noop() { } +/** + * Compares two numbers. + * @callback Comparator + * @param {number} a + * @param {number} b + * @returns {number} + */ +/** + * @param {number[]} arr + * @param {Comparator} cmp + */ +export function sortWith(arr, cmp) { + return arr.slice().sort(cmp); +} +//// [shared.js] +export function noop() { } +/** + * Two aliases from one comment. + * @typedef {number} A + * @typedef {string} B + */ +export function f() { } +//// [hosted.js] +export function noop() { } +/** + * Documents g, not N. + * @typedef {number} N + * @param {N} x + */ +export function g(x) { + return x; +} +//// [trailing.js] +export function noop() { } +/** + * Nothing follows this one. + * @typedef {Object} Trailing + * @property {number} x + */ +//// [imported.js] +export function noop() { } +/** + * Imports and defines in one comment. + * @import {Point} from "./typedef.js" + * @typedef {Point[]} Path + */ +export function h() { } +//// [preceded.js] +export function noop() { } +// not a JSDoc comment +/** + * @typedef {number} Q + */ +export function i() { } + + +//// [typedef.d.ts] +export declare function noop(): void; +/** + * A point in 2D space. + * @typedef {Object} Point + * @property {number} x + * @property {number} y + */ +export type Point = { + x: number; + y: number; +}; +/** + * @param {Point} p + */ +export declare function dist(p: Point): number; +//// [callback.d.ts] +export declare function noop(): void; +/** + * Compares two numbers. + * @callback Comparator + * @param {number} a + * @param {number} b + * @returns {number} + */ +export type Comparator = (a: number, b: number) => number; +/** + * @param {number[]} arr + * @param {Comparator} cmp + */ +export declare function sortWith(arr: number[], cmp: Comparator): number[]; +//// [shared.d.ts] +export declare function noop(): void; +/** + * Two aliases from one comment. + * @typedef {number} A + * @typedef {string} B + */ +export type A = number; +export type B = string; +export declare function f(): void; +//// [hosted.d.ts] +export declare function noop(): void; +export type N = number; +/** + * Documents g, not N. + * @typedef {number} N + * @param {N} x + */ +export declare function g(x: N): number; +//// [trailing.d.ts] +export declare function noop(): void; +/** + * Nothing follows this one. + * @typedef {Object} Trailing + * @property {number} x + */ +export type Trailing = { + x: number; +}; +//// [imported.d.ts] +export declare function noop(): void; +import type { Point } from "./typedef.js"; +/** + * Imports and defines in one comment. + * @import {Point} from "./typedef.js" + * @typedef {Point[]} Path + */ +export type Path = Point[]; +export declare function h(): void; +//// [preceded.d.ts] +export declare function noop(): void; +/** + * @typedef {number} Q + */ +export type Q = number; +export declare function i(): void; diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.symbols b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.symbols new file mode 100644 index 0000000000000..1ed309df2d385 --- /dev/null +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.symbols @@ -0,0 +1,121 @@ +//// [tests/cases/conformance/jsdoc/declarations/jsDeclarationsTypedefCommentPlacement.ts] //// + +=== typedef.js === +export function noop() {} +>noop : Symbol(noop, Decl(typedef.js, 0, 0)) + +/** + * A point in 2D space. + * @typedef {Object} Point + * @property {number} x + * @property {number} y + */ + +/** + * @param {Point} p + */ +export function dist(p) { +>dist : Symbol(dist, Decl(typedef.js, 0, 25)) +>p : Symbol(p, Decl(typedef.js, 12, 21)) + + return p.x; +>p.x : Symbol(x, Decl(typedef.js, 5, 3)) +>p : Symbol(p, Decl(typedef.js, 12, 21)) +>x : Symbol(x, Decl(typedef.js, 5, 3)) +} + +=== callback.js === +export function noop() {} +>noop : Symbol(noop, Decl(callback.js, 0, 0)) + +/** + * Compares two numbers. + * @callback Comparator + * @param {number} a + * @param {number} b + * @returns {number} + */ + +/** + * @param {number[]} arr + * @param {Comparator} cmp + */ +export function sortWith(arr, cmp) { +>sortWith : Symbol(sortWith, Decl(callback.js, 0, 25)) +>arr : Symbol(arr, Decl(callback.js, 14, 25)) +>cmp : Symbol(cmp, Decl(callback.js, 14, 29)) + + return arr.slice().sort(cmp); +>arr.slice().sort : Symbol(Array.sort, Decl(lib.es5.d.ts, --, --)) +>arr.slice : Symbol(Array.slice, Decl(lib.es5.d.ts, --, --)) +>arr : Symbol(arr, Decl(callback.js, 14, 25)) +>slice : Symbol(Array.slice, Decl(lib.es5.d.ts, --, --)) +>sort : Symbol(Array.sort, Decl(lib.es5.d.ts, --, --)) +>cmp : Symbol(cmp, Decl(callback.js, 14, 29)) +} + +=== shared.js === +export function noop() {} +>noop : Symbol(noop, Decl(shared.js, 0, 0)) + +/** + * Two aliases from one comment. + * @typedef {number} A + * @typedef {string} B + */ + +export function f() {} +>f : Symbol(f, Decl(shared.js, 0, 25)) + +=== hosted.js === +export function noop() {} +>noop : Symbol(noop, Decl(hosted.js, 0, 0)) + +/** + * Documents g, not N. + * @typedef {number} N + * @param {N} x + */ +export function g(x) { +>g : Symbol(g, Decl(hosted.js, 0, 25)) +>x : Symbol(x, Decl(hosted.js, 7, 18)) + + return x; +>x : Symbol(x, Decl(hosted.js, 7, 18)) +} + +=== trailing.js === +export function noop() {} +>noop : Symbol(noop, Decl(trailing.js, 0, 0)) + +/** + * Nothing follows this one. + * @typedef {Object} Trailing + * @property {number} x + */ + +=== imported.js === +export function noop() {} +>noop : Symbol(noop, Decl(imported.js, 0, 0)) + +/** + * Imports and defines in one comment. + * @import {Point} from "./typedef.js" + * @typedef {Point[]} Path + */ + +export function h() {} +>h : Symbol(h, Decl(imported.js, 0, 25)) + +=== preceded.js === +export function noop() {} +>noop : Symbol(noop, Decl(preceded.js, 0, 0)) + +// not a JSDoc comment +/** + * @typedef {number} Q + */ + +export function i() {} +>i : Symbol(i, Decl(preceded.js, 0, 25)) + diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.types b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.types new file mode 100644 index 0000000000000..3501d0948a4c4 --- /dev/null +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.types @@ -0,0 +1,123 @@ +//// [tests/cases/conformance/jsdoc/declarations/jsDeclarationsTypedefCommentPlacement.ts] //// + +=== typedef.js === +export function noop() {} +>noop : () => void + +/** + * A point in 2D space. + * @typedef {Object} Point + * @property {number} x + * @property {number} y + */ + +/** + * @param {Point} p + */ +export function dist(p) { +>dist : (p: Point) => number +>p : Point + + return p.x; +>p.x : number +>p : Point +>x : number +} + +=== callback.js === +export function noop() {} +>noop : () => void + +/** + * Compares two numbers. + * @callback Comparator + * @param {number} a + * @param {number} b + * @returns {number} + */ + +/** + * @param {number[]} arr + * @param {Comparator} cmp + */ +export function sortWith(arr, cmp) { +>sortWith : (arr: number[], cmp: Comparator) => number[] +>arr : number[] +>cmp : Comparator + + return arr.slice().sort(cmp); +>arr.slice().sort(cmp) : number[] +>arr.slice().sort : (compareFn?: ((a: number, b: number) => number) | undefined) => number[] +>arr.slice() : number[] +>arr.slice : (start?: number, end?: number) => number[] +>arr : number[] +>slice : (start?: number, end?: number) => number[] +>sort : (compareFn?: ((a: number, b: number) => number) | undefined) => number[] +>cmp : Comparator +} + +=== shared.js === +export function noop() {} +>noop : () => void + +/** + * Two aliases from one comment. + * @typedef {number} A + * @typedef {string} B + */ + +export function f() {} +>f : () => void + +=== hosted.js === +export function noop() {} +>noop : () => void + +/** + * Documents g, not N. + * @typedef {number} N + * @param {N} x + */ +export function g(x) { +>g : (x: N) => number +>x : number + + return x; +>x : number +} + +=== trailing.js === +export function noop() {} +>noop : () => void + +/** + * Nothing follows this one. + * @typedef {Object} Trailing + * @property {number} x + */ + +=== imported.js === +export function noop() {} +>noop : () => void + +/** + * Imports and defines in one comment. + * @import {Point} from "./typedef.js" + * @typedef {Point[]} Path + */ + +export function h() {} +>h : () => void + +=== preceded.js === +export function noop() {} +>noop : () => void + +// not a JSDoc comment +/** + * @typedef {number} Q + */ + +export function i() {} +>i : () => void + diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefDescriptionsPreserved.js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefDescriptionsPreserved.js index 2e76c81fde174..7616893d5b700 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefDescriptionsPreserved.js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefDescriptionsPreserved.js @@ -53,6 +53,14 @@ type FooOptions = { */ baz: string; }; +/** + * Multiline + * Options + * for Foo <------------ + * @typedef {Object} BarOptions + * @property {boolean} bar - Marvin K Mooney + * @property {string} baz - Sylvester McMonkey McBean + */ type BarOptions = { /** * - Marvin K Mooney @@ -63,11 +71,3 @@ type BarOptions = { */ baz: string; }; -/** - * Multiline - * Options - * for Foo <------------ - * @typedef {Object} BarOptions - * @property {boolean} bar - Marvin K Mooney - * @property {string} baz - Sylvester McMonkey McBean - */ diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefPropertyAndExportAssignment.js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefPropertyAndExportAssignment.js index 8999557336040..6d353f2155bb5 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefPropertyAndExportAssignment.js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefPropertyAndExportAssignment.js @@ -104,7 +104,6 @@ module.exports = MainThreadTasks; //// [module.d.ts] -/** @typedef {'parseHTML'|'styleLayout'} TaskGroupIds */ declare const _exports: { taskGroups: { parseHTML: { @@ -119,7 +118,14 @@ declare const _exports: { taskNameToGroup: Record; }; export = _exports; +/** @typedef {'parseHTML'|'styleLayout'} TaskGroupIds */ export type TaskGroupIds = 'parseHTML' | 'styleLayout'; +/** + * @typedef TaskGroup + * @property {TaskGroupIds} id + * @property {string} label + * @property {string[]} traceEventNames + */ export type TaskGroup = { id: TaskGroupIds; label: string; @@ -127,23 +133,23 @@ export type TaskGroup = { }; //// [index.d.ts] export = MainThreadTasks; +/** @typedef {import('./module.js').TaskGroup} TaskGroup */ export type TaskGroup = import('./module.js').TaskGroup; +/** + * @typedef TaskNode + * @prop {TaskNode[]} children + * @prop {TaskNode|undefined} parent + * @prop {TaskGroup} group + */ export type TaskNode = { children: TaskNode[]; parent: TaskNode | undefined; group: TaskGroup; }; +/** @typedef {{timers: Map}} PriorTaskData */ export type PriorTaskData = { timers: Map; }; -/** @typedef {import('./module.js').TaskGroup} TaskGroup */ -/** - * @typedef TaskNode - * @prop {TaskNode[]} children - * @prop {TaskNode|undefined} parent - * @prop {TaskGroup} group - */ -/** @typedef {{timers: Map}} PriorTaskData */ declare class MainThreadTasks { /** * @param {TaskGroup} x diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsUniqueSymbolUsage.js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsUniqueSymbolUsage.js index 35f951bdfd045..b3db55e388b29 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsUniqueSymbolUsage.js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsUniqueSymbolUsage.js @@ -20,12 +20,12 @@ export function b(value) { //// [a.d.ts] export declare const kSymbol: unique symbol; -export type WithSymbol = { - [kSymbol]: true; -}; /** * @typedef {{[kSymbol]: true}} WithSymbol */ +export type WithSymbol = { + [kSymbol]: true; +}; //// [b.d.ts] /** * @returns {import('./a').WithSymbol} diff --git a/tsc/testdata/baselines/reference/conformance/linkTagEmit1.js b/tsc/testdata/baselines/reference/conformance/linkTagEmit1.js index b03dba9647d9e..b5c4f8f8d3c04 100644 --- a/tsc/testdata/baselines/reference/conformance/linkTagEmit1.js +++ b/tsc/testdata/baselines/reference/conformance/linkTagEmit1.js @@ -49,12 +49,12 @@ var see3 = true; //// [linkTagEmit1.d.ts] /** @typedef {number} N */ +type N = number; /** * @typedef {Object} D1 * @property {1} e Just link to {@link NS.R} this time * @property {1} m Wyatt Earp loved {@link N integers} I bet. */ -type N = number; type D1 = { /** * Just link to {@link NS.R} this time @@ -73,5 +73,5 @@ type Z = number; declare function computeCommonSourceDirectoryOfFilenames(integer: number): number; /** {@link https://hvad} */ declare var see3: boolean; -type Attempt = number; /** @typedef {number} Attempt {@link https://wat} {@linkcode I think lingcod is better} {@linkplain or lutefisk}*/ +type Attempt = number; diff --git a/tsc/testdata/baselines/reference/conformance/recursiveTypeReferences2.js b/tsc/testdata/baselines/reference/conformance/recursiveTypeReferences2.js index cb238bde424b5..bd343ff7ced45 100644 --- a/tsc/testdata/baselines/reference/conformance/recursiveTypeReferences2.js +++ b/tsc/testdata/baselines/reference/conformance/recursiveTypeReferences2.js @@ -57,12 +57,12 @@ const p = {}; //// [bug39372.d.ts] /** @typedef {ReadonlyArray} JsonArray */ -/** @typedef {{ readonly [key: string]: Json }} JsonRecord */ -/** @typedef {boolean | number | string | null | JsonRecord | JsonArray | readonly []} Json */ type JsonArray = ReadonlyArray; +/** @typedef {{ readonly [key: string]: Json }} JsonRecord */ type JsonRecord = { readonly [key: string]: Json; }; +/** @typedef {boolean | number | string | null | JsonRecord | JsonArray | readonly []} Json */ type Json = boolean | number | string | null | JsonRecord | JsonArray | readonly []; type XMLObject = { $A: { [K in keyof T]?: XMLObject[]; }; diff --git a/tsc/testdata/baselines/reference/conformance/templateInsideCallback.js b/tsc/testdata/baselines/reference/conformance/templateInsideCallback.js index dc6fe5579e022..06756655abeb9 100644 --- a/tsc/testdata/baselines/reference/conformance/templateInsideCallback.js +++ b/tsc/testdata/baselines/reference/conformance/templateInsideCallback.js @@ -113,28 +113,35 @@ function flatMap(array, iterable = identity) { //// [templateInsideCallback.d.ts] -type Oops = { - a: T; - b: T; -}; -type Call = (x: T) => T; /** * @typedef Oops * @template T * @property {T} a * @property {T} b */ +type Oops = { + a: T; + b: T; +}; /** * @callback Call * @template T * @param {T} x * @returns {T} */ +type Call = (x: T) => T; /** * @template T * @type {Call} */ declare const identity: Call; +/** + * @typedef Nested + * @property {Object} oh + * @property {number} oh.no + * @template T + * @property {string} oh.noooooo + */ type Nested = { oh: { no: number; diff --git a/tsc/testdata/baselines/reference/conformance/typedefModuleExportsIndirect1.js b/tsc/testdata/baselines/reference/conformance/typedefModuleExportsIndirect1.js index 835bca08c60c4..623d05d000c9e 100644 --- a/tsc/testdata/baselines/reference/conformance/typedefModuleExportsIndirect1.js +++ b/tsc/testdata/baselines/reference/conformance/typedefModuleExportsIndirect1.js @@ -24,14 +24,14 @@ var c; //// [typedefModuleExportsIndirect1.d.ts] export = dummy; +/** @typedef {{ a: 1, m: 1 }} C */ export type C = { a: 1; m: 1; }; -/** @typedef {{ a: 1, m: 1 }} C */ declare const dummy = 0; //// [use.d.ts] -type C = import('./typedefModuleExportsIndirect1').C; /** @typedef {import('./typedefModuleExportsIndirect1').C} C */ +type C = import('./typedefModuleExportsIndirect1').C; /** @type {C} */ declare var c: C; diff --git a/tsc/testdata/baselines/reference/conformance/typedefModuleExportsIndirect2.js b/tsc/testdata/baselines/reference/conformance/typedefModuleExportsIndirect2.js index 66fdb1e30a5f8..df5a8eafc88cf 100644 --- a/tsc/testdata/baselines/reference/conformance/typedefModuleExportsIndirect2.js +++ b/tsc/testdata/baselines/reference/conformance/typedefModuleExportsIndirect2.js @@ -24,14 +24,14 @@ var c; //// [typedefModuleExportsIndirect2.d.ts] export = f; +/** @typedef {{ a: 1, m: 1 }} C */ export type C = { a: 1; m: 1; }; -/** @typedef {{ a: 1, m: 1 }} C */ declare const f: () => void; //// [use.d.ts] -type C = import('./typedefModuleExportsIndirect2').C; /** @typedef {import('./typedefModuleExportsIndirect2').C} C */ +type C = import('./typedefModuleExportsIndirect2').C; /** @type {C} */ declare var c: C; diff --git a/tsc/testdata/baselines/reference/conformance/typedefModuleExportsIndirect3.js b/tsc/testdata/baselines/reference/conformance/typedefModuleExportsIndirect3.js index 5d7dae4e723b9..269952d4573dc 100644 --- a/tsc/testdata/baselines/reference/conformance/typedefModuleExportsIndirect3.js +++ b/tsc/testdata/baselines/reference/conformance/typedefModuleExportsIndirect3.js @@ -24,14 +24,14 @@ var c; //// [typedefModuleExportsIndirect3.d.ts] export = o; +/** @typedef {{ a: 1, m: 1 }} C */ export type C = { a: 1; m: 1; }; -/** @typedef {{ a: 1, m: 1 }} C */ declare const o: {}; //// [use.d.ts] -type C = import('./typedefModuleExportsIndirect3').C; /** @typedef {import('./typedefModuleExportsIndirect3').C} C */ +type C = import('./typedefModuleExportsIndirect3').C; /** @type {C} */ declare var c: C; diff --git a/tsc/testdata/baselines/reference/conformance/typedefOnSemicolonClassElement.js b/tsc/testdata/baselines/reference/conformance/typedefOnSemicolonClassElement.js index 5bfd2f8f0c529..22c1ed2c07fc9 100644 --- a/tsc/testdata/baselines/reference/conformance/typedefOnSemicolonClassElement.js +++ b/tsc/testdata/baselines/reference/conformance/typedefOnSemicolonClassElement.js @@ -21,6 +21,7 @@ export class Preferences { //// [typedefOnSemicolonClassElement.d.ts] +/** @typedef {string} A */ export type A = string; export declare class Preferences { /** @type {A} */ diff --git a/tsc/testdata/baselines/reference/conformance/typedefOnStatements.js b/tsc/testdata/baselines/reference/conformance/typedefOnStatements.js index 60373b4f8996f..dcf0e627fb57f 100644 --- a/tsc/testdata/baselines/reference/conformance/typedefOnStatements.js +++ b/tsc/testdata/baselines/reference/conformance/typedefOnStatements.js @@ -159,54 +159,71 @@ function proof(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) { //// [typedefOnStatements.d.ts] +/** @typedef {{a: string}} A */ type A = { a: string; }; +/** @typedef {{ b: string }} B */ type B = { b: string; }; +/** @typedef {{ c: string }} C */ type C = { c: string; }; +/** @typedef {{ d: string }} D */ type D = { d: string; }; +/** @typedef {{ e: string }} E */ type E = { e: string; }; +/** @typedef {{ f: string }} F */ type F = { f: string; }; +/** @typedef {{ g: string }} G */ type G = { g: string; }; +/** @typedef {{ h: string }} H */ type H = { h: string; }; +/** @typedef {{ i: string }} I */ type I = { i: string; }; +/** @typedef {{ j: string }} J */ type J = { j: string; }; +/** @typedef {{ k: string }} K */ type K = { k: string; }; +/** @typedef {{ l: string }} L */ type L = { l: string; }; +/** @typedef {{ m: string }} M */ type M = { m: string; }; +/** @typedef {{ n: string }} N */ type N = { n: string; }; +/** @typedef {{ o: string }} O */ type O = { o: string; }; +/** @typedef {{ p: string }} P */ type P = { p: string; }; +/** @typedef {{ q: string }} Q */ type Q = { q: string; }; diff --git a/tsc/testdata/baselines/reference/tsbuild/javascriptProjectEmit/loads-js-based-projects-and-emits-them-correctly.js b/tsc/testdata/baselines/reference/tsbuild/javascriptProjectEmit/loads-js-based-projects-and-emits-them-correctly.js index fec3153558361..25cd8eb4af983 100644 --- a/tsc/testdata/baselines/reference/tsbuild/javascriptProjectEmit/loads-js-based-projects-and-emits-them-correctly.js +++ b/tsc/testdata/baselines/reference/tsbuild/javascriptProjectEmit/loads-js-based-projects-and-emits-them-correctly.js @@ -265,7 +265,7 @@ export function getVar() { } //// [/home/src/workspaces/lib/sub-project-2/tsconfig.tsbuildinfo] *new* -{"version":"FakeTSVersion","root":[4],"fileNames":["lib.es2025.full.d.ts","../common/nominal.d.ts","../sub-project/index.d.ts","../../solution/sub-project-2/index.js"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},"81ac3a6e47fc4be420cd544e175d838d-/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\ndeclare const _exports: {};\nexport = _exports;\nexport type Nominal = T & {};\n","225285a996cc5c4120877a377890d79e-import { Nominal } from '../common/nominal';\nexport type MyNominal = Nominal;\n/**\n * @typedef {Nominal} MyNominal\n */ \n",{"version":"db2a90e082fd17d65127bda69975a727-import { MyNominal } from '../sub-project/index';\n\nconst variable = {\n key: /** @type {MyNominal} */('value'),\n};\n\n/**\n * @return {keyof typeof variable}\n */\nexport function getVar() {\n return 'key';\n}","signature":"0620ca4a9fe7036c93bd1594ffccf8eb-import { MyNominal } from '../sub-project/index';\ndeclare const variable: {\n key: MyNominal;\n};\n/**\n * @return {keyof typeof variable}\n */\nexport declare function getVar(): keyof typeof variable;\nexport {};\n","impliedNodeFormat":1}],"fileIdsList":[[2],[3]],"options":{"allowJs":true,"checkJs":true,"composite":true,"declaration":true,"outDir":"..","rootDir":"../../solution","skipLibCheck":true},"referencedMap":[[3,1],[4,2]],"semanticDiagnosticsPerFile":[[4,[{"pos":9,"end":18,"code":18042,"category":1,"messageKey":"_0_is_a_type_and_cannot_be_imported_in_JavaScript_files_Use_1_in_a_JSDoc_type_annotation_18042","messageArgs":["MyNominal","import(\"../sub-project/index\").MyNominal"]}]]],"latestChangedDtsFile":"./index.d.ts"} +{"version":"FakeTSVersion","root":[4],"fileNames":["lib.es2025.full.d.ts","../common/nominal.d.ts","../sub-project/index.d.ts","../../solution/sub-project-2/index.js"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},"81ac3a6e47fc4be420cd544e175d838d-/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\ndeclare const _exports: {};\nexport = _exports;\nexport type Nominal = T & {};\n","5fa17a470c0fbdd3d822f5e7ff2044b0-import { Nominal } from '../common/nominal';\n/**\n * @typedef {Nominal} MyNominal\n */ \nexport type MyNominal = Nominal;\n",{"version":"db2a90e082fd17d65127bda69975a727-import { MyNominal } from '../sub-project/index';\n\nconst variable = {\n key: /** @type {MyNominal} */('value'),\n};\n\n/**\n * @return {keyof typeof variable}\n */\nexport function getVar() {\n return 'key';\n}","signature":"0620ca4a9fe7036c93bd1594ffccf8eb-import { MyNominal } from '../sub-project/index';\ndeclare const variable: {\n key: MyNominal;\n};\n/**\n * @return {keyof typeof variable}\n */\nexport declare function getVar(): keyof typeof variable;\nexport {};\n","impliedNodeFormat":1}],"fileIdsList":[[2],[3]],"options":{"allowJs":true,"checkJs":true,"composite":true,"declaration":true,"outDir":"..","rootDir":"../../solution","skipLibCheck":true},"referencedMap":[[3,1],[4,2]],"semanticDiagnosticsPerFile":[[4,[{"pos":9,"end":18,"code":18042,"category":1,"messageKey":"_0_is_a_type_and_cannot_be_imported_in_JavaScript_files_Use_1_in_a_JSDoc_type_annotation_18042","messageArgs":["MyNominal","import(\"../sub-project/index\").MyNominal"]}]]],"latestChangedDtsFile":"./index.d.ts"} //// [/home/src/workspaces/lib/sub-project-2/tsconfig.tsbuildinfo.readable.baseline.txt] *new* { "version": "FakeTSVersion", @@ -304,8 +304,8 @@ export function getVar() { }, { "fileName": "../sub-project/index.d.ts", - "version": "225285a996cc5c4120877a377890d79e-import { Nominal } from '../common/nominal';\nexport type MyNominal = Nominal;\n/**\n * @typedef {Nominal} MyNominal\n */ \n", - "signature": "225285a996cc5c4120877a377890d79e-import { Nominal } from '../common/nominal';\nexport type MyNominal = Nominal;\n/**\n * @typedef {Nominal} MyNominal\n */ \n", + "version": "5fa17a470c0fbdd3d822f5e7ff2044b0-import { Nominal } from '../common/nominal';\n/**\n * @typedef {Nominal} MyNominal\n */ \nexport type MyNominal = Nominal;\n", + "signature": "5fa17a470c0fbdd3d822f5e7ff2044b0-import { Nominal } from '../common/nominal';\n/**\n * @typedef {Nominal} MyNominal\n */ \nexport type MyNominal = Nominal;\n", "impliedNodeFormat": "CommonJS" }, { @@ -368,10 +368,10 @@ export function getVar() { } //// [/home/src/workspaces/lib/sub-project/index.d.ts] *new* import { Nominal } from '../common/nominal'; -export type MyNominal = Nominal; /** * @typedef {Nominal} MyNominal */ +export type MyNominal = Nominal; //// [/home/src/workspaces/lib/sub-project/index.js] *new* import { Nominal } from '../common/nominal'; @@ -380,7 +380,7 @@ import { Nominal } from '../common/nominal'; */ //// [/home/src/workspaces/lib/sub-project/tsconfig.tsbuildinfo] *new* -{"version":"FakeTSVersion","root":[3],"fileNames":["lib.es2025.full.d.ts","../common/nominal.d.ts","../../solution/sub-project/index.js"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},"81ac3a6e47fc4be420cd544e175d838d-/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\ndeclare const _exports: {};\nexport = _exports;\nexport type Nominal = T & {};\n",{"version":"00b7836eaf1e026f7764b7be6efcc8f5-import { Nominal } from '../common/nominal';\n\n/**\n * @typedef {Nominal} MyNominal\n */","signature":"225285a996cc5c4120877a377890d79e-import { Nominal } from '../common/nominal';\nexport type MyNominal = Nominal;\n/**\n * @typedef {Nominal} MyNominal\n */ \n","impliedNodeFormat":1}],"fileIdsList":[[2]],"options":{"allowJs":true,"checkJs":true,"composite":true,"declaration":true,"outDir":"..","rootDir":"../../solution","skipLibCheck":true},"referencedMap":[[3,1]],"semanticDiagnosticsPerFile":[[3,[{"pos":9,"end":16,"code":18042,"category":1,"messageKey":"_0_is_a_type_and_cannot_be_imported_in_JavaScript_files_Use_1_in_a_JSDoc_type_annotation_18042","messageArgs":["Nominal","import(\"../common/nominal\").Nominal"]}]]],"latestChangedDtsFile":"./index.d.ts"} +{"version":"FakeTSVersion","root":[3],"fileNames":["lib.es2025.full.d.ts","../common/nominal.d.ts","../../solution/sub-project/index.js"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},"81ac3a6e47fc4be420cd544e175d838d-/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\ndeclare const _exports: {};\nexport = _exports;\nexport type Nominal = T & {};\n",{"version":"00b7836eaf1e026f7764b7be6efcc8f5-import { Nominal } from '../common/nominal';\n\n/**\n * @typedef {Nominal} MyNominal\n */","signature":"5fa17a470c0fbdd3d822f5e7ff2044b0-import { Nominal } from '../common/nominal';\n/**\n * @typedef {Nominal} MyNominal\n */ \nexport type MyNominal = Nominal;\n","impliedNodeFormat":1}],"fileIdsList":[[2]],"options":{"allowJs":true,"checkJs":true,"composite":true,"declaration":true,"outDir":"..","rootDir":"../../solution","skipLibCheck":true},"referencedMap":[[3,1]],"semanticDiagnosticsPerFile":[[3,[{"pos":9,"end":16,"code":18042,"category":1,"messageKey":"_0_is_a_type_and_cannot_be_imported_in_JavaScript_files_Use_1_in_a_JSDoc_type_annotation_18042","messageArgs":["Nominal","import(\"../common/nominal\").Nominal"]}]]],"latestChangedDtsFile":"./index.d.ts"} //// [/home/src/workspaces/lib/sub-project/tsconfig.tsbuildinfo.readable.baseline.txt] *new* { "version": "FakeTSVersion", @@ -419,11 +419,11 @@ import { Nominal } from '../common/nominal'; { "fileName": "../../solution/sub-project/index.js", "version": "00b7836eaf1e026f7764b7be6efcc8f5-import { Nominal } from '../common/nominal';\n\n/**\n * @typedef {Nominal} MyNominal\n */", - "signature": "225285a996cc5c4120877a377890d79e-import { Nominal } from '../common/nominal';\nexport type MyNominal = Nominal;\n/**\n * @typedef {Nominal} MyNominal\n */ \n", + "signature": "5fa17a470c0fbdd3d822f5e7ff2044b0-import { Nominal } from '../common/nominal';\n/**\n * @typedef {Nominal} MyNominal\n */ \nexport type MyNominal = Nominal;\n", "impliedNodeFormat": "CommonJS", "original": { "version": "00b7836eaf1e026f7764b7be6efcc8f5-import { Nominal } from '../common/nominal';\n\n/**\n * @typedef {Nominal} MyNominal\n */", - "signature": "225285a996cc5c4120877a377890d79e-import { Nominal } from '../common/nominal';\nexport type MyNominal = Nominal;\n/**\n * @typedef {Nominal} MyNominal\n */ \n", + "signature": "5fa17a470c0fbdd3d822f5e7ff2044b0-import { Nominal } from '../common/nominal';\n/**\n * @typedef {Nominal} MyNominal\n */ \nexport type MyNominal = Nominal;\n", "impliedNodeFormat": 1 } } diff --git a/tsc/testdata/tests/cases/conformance/jsdoc/declarations/jsDeclarationsTypedefCommentPlacement.ts b/tsc/testdata/tests/cases/conformance/jsdoc/declarations/jsDeclarationsTypedefCommentPlacement.ts new file mode 100644 index 0000000000000..672b0582454c6 --- /dev/null +++ b/tsc/testdata/tests/cases/conformance/jsdoc/declarations/jsDeclarationsTypedefCommentPlacement.ts @@ -0,0 +1,100 @@ +// @target: es2015 +// @allowJs: true +// @checkJs: true +// @outDir: ./out +// @declaration: true +// @filename: typedef.js + +export function noop() {} + +/** + * A point in 2D space. + * @typedef {Object} Point + * @property {number} x + * @property {number} y + */ + +/** + * @param {Point} p + */ +export function dist(p) { + return p.x; +} + +// @filename: callback.js + +export function noop() {} + +/** + * Compares two numbers. + * @callback Comparator + * @param {number} a + * @param {number} b + * @returns {number} + */ + +/** + * @param {number[]} arr + * @param {Comparator} cmp + */ +export function sortWith(arr, cmp) { + return arr.slice().sort(cmp); +} + +// @filename: shared.js + +export function noop() {} + +/** + * Two aliases from one comment. + * @typedef {number} A + * @typedef {string} B + */ + +export function f() {} + +// @filename: hosted.js + +export function noop() {} + +/** + * Documents g, not N. + * @typedef {number} N + * @param {N} x + */ +export function g(x) { + return x; +} + +// @filename: trailing.js + +export function noop() {} + +/** + * Nothing follows this one. + * @typedef {Object} Trailing + * @property {number} x + */ + +// @filename: imported.js + +export function noop() {} + +/** + * Imports and defines in one comment. + * @import {Point} from "./typedef.js" + * @typedef {Point[]} Path + */ + +export function h() {} + +// @filename: preceded.js + +export function noop() {} + +// not a JSDoc comment +/** + * @typedef {number} Q + */ + +export function i() {} From 922a24dff052a88eda940946c04266c6e9b45b14 Mon Sep 17 00:00:00 2001 From: Eugene Kalinin Date: Sat, 5 Sep 2026 19:36:52 +0300 Subject: [PATCH 2/3] fix(declarations): claim JSDoc comments for @template, @import and @overload Review follow-up on the reparsed-JSDoc comment ownership. `@template` was rejected by the tag predicate, so a generic alias declared with `@template` + `@typedef`/`@callback` still lost its comment. The parser hands every `@template` in such a comment to the type being declared and leaves none for the host (gatherTypeParameters), so mirror that: a template tag is unhosted exactly when its comment also declares a type. `@import` and `@overload` were listed as claimable but could never claim, because reparseUnhosted recorded no JSDocInfo for the declarations it builds from them and their JSDoc lookup was always empty. Register the comment the way the typedef and callback cases already do. This also gives overload signatures their documentation in signature help, which was previously dropped. Stop the detached-comment run at a claimed comment. Its owner is emitted later, so detaching the rest of the run printed those comments ahead of the owner and put them out of source order. --- tsc/internal/parser/reparser.go | 7 +- tsc/internal/printer/printer.go | 9 +- .../transformers/declarations/transform.go | 16 +- .../compiler/jsDocGenericOverloads.errors.txt | 41 +++++ .../compiler/jsFileFunctionOverloads.js | 15 ++ .../reference/compiler/typedefHoisting.js | 2 +- .../conformance/importTag15(module=es2015).js | 2 +- .../conformance/importTag15(module=esnext).js | 2 +- ...ntsGenericsSerialization(target=es2015).js | 2 +- .../conformance/jsDeclarationsTypeAliases.js | 8 +- .../jsDeclarationsTypedefCommentPlacement.js | 145 +++++++++++++++++- ...eclarationsTypedefCommentPlacement.symbols | 69 +++++++++ ...sDeclarationsTypedefCommentPlacement.types | 72 +++++++++ .../conformance/jsdocTemplateTagDefault.js | 10 +- .../jsdocVariadicInOverload.errors.txt | 52 +++++++ .../reference/conformance/overloadTag1.js | 11 ++ .../conformance/recursiveTypeReferences2.js | 22 +-- .../conformance/templateInsideCallback.js | 14 ++ .../quickInfoJsDocTags13.baseline | 18 +++ .../quickInfoJsDocTags13VS.baseline | 18 +++ ...based-projects-and-emits-them-correctly.js | 22 +-- .../jsDeclarationsTypedefCommentPlacement.ts | 59 +++++++ 22 files changed, 575 insertions(+), 41 deletions(-) create mode 100644 tsc/testdata/baselines/reference/compiler/jsDocGenericOverloads.errors.txt create mode 100644 tsc/testdata/baselines/reference/conformance/jsdocVariadicInOverload.errors.txt diff --git a/tsc/internal/parser/reparser.go b/tsc/internal/parser/reparser.go index bdebd40ade98d..493f8d0fee126 100644 --- a/tsc/internal/parser/reparser.go +++ b/tsc/internal/parser/reparser.go @@ -134,11 +134,16 @@ func (p *Parser) reparseUnhosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Nod p.addDeepCloneReparse(importTag.Attributes), ) p.finishReparsedNode(importDeclaration, tag) + p.jsdocInfos = append(p.jsdocInfos, JSDocInfo{parent: importDeclaration, jsDocs: []*ast.Node{jsDoc}}) + importDeclaration.Flags |= ast.NodeFlagsHasJSDoc p.reparseList = append(p.reparseList, importDeclaration) case ast.KindJSDocOverloadTag: // Create overload signatures only for function, method, and constructor declarations outside object literals if (ast.IsFunctionDeclaration(parent) || ast.IsMethodDeclaration(parent) || ast.IsConstructorDeclaration(parent)) && p.parsingContexts&(1< 0 && core.Every(tags.Nodes, isUnhostedJSDocTag) + if tags == nil || len(tags.Nodes) == 0 { + return false + } + declaresType := core.Some(tags.Nodes, func(tag *ast.Node) bool { + return ast.IsJSDocTypedefTag(tag) || ast.IsJSDocCallbackTag(tag) + }) + return core.Every(tags.Nodes, func(tag *ast.Node) bool { return isUnhostedJSDocTag(tag, declaresType) }) } // A JSDoc node starts at the full start of the node it documents rather than at its own `/**`, so diff --git a/tsc/testdata/baselines/reference/compiler/jsDocGenericOverloads.errors.txt b/tsc/testdata/baselines/reference/compiler/jsDocGenericOverloads.errors.txt new file mode 100644 index 0000000000000..312535cc2fe33 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/jsDocGenericOverloads.errors.txt @@ -0,0 +1,41 @@ +main.js(21,37): error TS8024: JSDoc '@param' tag has name 'c', but there is no parameter with that name. + + +==== main.js (1 errors) ==== + const createElementC = /** + * @template {keyof HTMLElementTagNameMap} T + * @param {T}t + * @param {NodeList|HTMLCollection=}c + * + * @overload + * @param {T}t + * @return {HTMLElementTagNameMap[T]} + * + * @overload + * @param {T}t + * @param {NodeList|HTMLCollection}c + * @return {HTMLElementTagNameMap[T]} + */(t, c) => { + /* ... omitted for brevity ... */ return document.createElement(t) + } + + /** + * @template {keyof HTMLElementTagNameMap} T + * @param {T}t + * @param {NodeList|HTMLCollection=}c + ~ +!!! error TS8024: JSDoc '@param' tag has name 'c', but there is no parameter with that name. + * + * @overload + * @param {T}t + * @return {HTMLElementTagNameMap[T]} + * + * @overload + * @param {T}t + * @param {NodeList|HTMLCollection}c + * @return {HTMLElementTagNameMap[T]} + */ + function createElementF(t, c) { + /* ... omitted for brevity ... */ return document.createElement(t) + } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/jsFileFunctionOverloads.js b/tsc/testdata/baselines/reference/compiler/jsFileFunctionOverloads.js index 72fee946e753c..5ca184c33b9ca 100644 --- a/tsc/testdata/baselines/reference/compiler/jsFileFunctionOverloads.js +++ b/tsc/testdata/baselines/reference/compiler/jsFileFunctionOverloads.js @@ -120,8 +120,23 @@ function flatMap(array, iterable = identity) { //// [jsFileFunctionOverloads.d.ts] +/** + * @overload + * @param {number} x + * @returns {'number'} + */ declare function getTypeName(x: number): 'number'; +/** + * @overload + * @param {string} x + * @returns {'string'} + */ declare function getTypeName(x: string): 'string'; +/** + * @overload + * @param {boolean} x + * @returns {'boolean'} + */ declare function getTypeName(x: boolean): 'boolean'; /** * @template T diff --git a/tsc/testdata/baselines/reference/compiler/typedefHoisting.js b/tsc/testdata/baselines/reference/compiler/typedefHoisting.js index 2604de95a15b1..6a0abf6821f81 100644 --- a/tsc/testdata/baselines/reference/compiler/typedefHoisting.js +++ b/tsc/testdata/baselines/reference/compiler/typedefHoisting.js @@ -23,11 +23,11 @@ export {} export type Bar = string; export {}; //// [x.d.ts] +/** @import {Bar} from "./y" */ import type { Bar } from "./y"; /** @typedef {Bar[]} Bars */ type Bars = Bar[]; declare class C { - /** @import {Bar} from "./y" */ /** @type {Bars} */ foo: Bars; bar(/** @type {Bar} */ x: Bar): string; diff --git a/tsc/testdata/baselines/reference/conformance/importTag15(module=es2015).js b/tsc/testdata/baselines/reference/conformance/importTag15(module=es2015).js index 2ade94c80ebc1..099fec4f4e71b 100644 --- a/tsc/testdata/baselines/reference/conformance/importTag15(module=es2015).js +++ b/tsc/testdata/baselines/reference/conformance/importTag15(module=es2015).js @@ -18,7 +18,7 @@ export interface I { } //// [1.d.ts] /** @import { I } from './0' with { type: "json" } */ -/** @import * as foo from './0' with { type: "json" } */ import type { I } from './0' with { type: "json" }; +/** @import * as foo from './0' with { type: "json" } */ /** @param {I} a */ declare function f(a: I): void; diff --git a/tsc/testdata/baselines/reference/conformance/importTag15(module=esnext).js b/tsc/testdata/baselines/reference/conformance/importTag15(module=esnext).js index 2ade94c80ebc1..099fec4f4e71b 100644 --- a/tsc/testdata/baselines/reference/conformance/importTag15(module=esnext).js +++ b/tsc/testdata/baselines/reference/conformance/importTag15(module=esnext).js @@ -18,7 +18,7 @@ export interface I { } //// [1.d.ts] /** @import { I } from './0' with { type: "json" } */ -/** @import * as foo from './0' with { type: "json" } */ import type { I } from './0' with { type: "json" }; +/** @import * as foo from './0' with { type: "json" } */ /** @param {I} a */ declare function f(a: I): void; diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsClassImplementsGenericsSerialization(target=es2015).js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsClassImplementsGenericsSerialization(target=es2015).js index a162e36a7e401..e2149d92f9605 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsClassImplementsGenericsSerialization(target=es2015).js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsClassImplementsGenericsSerialization(target=es2015).js @@ -65,8 +65,8 @@ export declare class Encoder implements IEncoder { */ encode(value: T): Uint8Array; } -export type IEncoder = import('./interface').Encoder; /** * @template T * @typedef {import('./interface').Encoder} IEncoder */ +export type IEncoder = import('./interface').Encoder; diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypeAliases.js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypeAliases.js index 5a06d75a5e466..31a895531fe66 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypeAliases.js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypeAliases.js @@ -118,14 +118,13 @@ export type PropName = string | number | symbol; * @returns {string} */ export type NumberToStringCb = (a: number) => string; -export type MixinName = T & { - name: string; -}; -export type Identity = (x: T) => T; /** * @template T * @typedef {T & {name: string}} MixinName */ +export type MixinName = T & { + name: string; +}; /** * Identity function * @@ -134,6 +133,7 @@ export type Identity = (x: T) => T; * @param {T} x * @returns {T} */ +export type Identity = (x: T) => T; //// [mixed.d.ts] declare const _exports: { doTheThing: typeof doTheThing; diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.js index f06ba8f26595c..d8704513623a4 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.js @@ -89,6 +89,61 @@ export function noop() {} export function i() {} +//// [generic.js] +export function noop() {} + +/** + * A named mixin. + * @template T + * @typedef {T & {name: string}} MixinName + */ + +/** + * Identity. + * @template T + * @callback Identity + * @param {T} x + * @returns {T} + */ + +export function j() {} + +//// [templateOnHost.js] +export function noop() {} + +/** + * Documents k, not a type. + * @template T + * @param {T} x + */ +export function k(x) { return x; } + +//// [standaloneImport.js] +export function noop() {} + +/** + * Brings Point into scope. + * @import {Point} from "./typedef.js" + */ + +/** @type {Point} */ +export const p = { x: 0, y: 0 }; + +//// [overload.js] +export function noop() {} + +/** + * Takes a string. + * @overload + * @param {string} x + * @returns {string} + */ + +/** + * @param {any} x + */ +export function l(x) { return x; } + //// [typedef.js] export function noop() { } @@ -160,6 +215,49 @@ export function noop() { } * @typedef {number} Q */ export function i() { } +//// [generic.js] +export function noop() { } +/** + * A named mixin. + * @template T + * @typedef {T & {name: string}} MixinName + */ +/** + * Identity. + * @template T + * @callback Identity + * @param {T} x + * @returns {T} + */ +export function j() { } +//// [templateOnHost.js] +export function noop() { } +/** + * Documents k, not a type. + * @template T + * @param {T} x + */ +export function k(x) { return x; } +//// [standaloneImport.js] +export function noop() { } +/** + * Brings Point into scope. + * @import {Point} from "./typedef.js" + */ +/** @type {Point} */ +export const p = { x: 0, y: 0 }; +//// [overload.js] +export function noop() { } +/** + * Takes a string. + * @overload + * @param {string} x + * @returns {string} + */ +/** + * @param {any} x + */ +export function l(x) { return x; } //// [typedef.d.ts] @@ -224,12 +322,12 @@ export type Trailing = { }; //// [imported.d.ts] export declare function noop(): void; -import type { Point } from "./typedef.js"; /** * Imports and defines in one comment. * @import {Point} from "./typedef.js" * @typedef {Point[]} Path */ +import type { Point } from "./typedef.js"; export type Path = Point[]; export declare function h(): void; //// [preceded.d.ts] @@ -239,3 +337,48 @@ export declare function noop(): void; */ export type Q = number; export declare function i(): void; +//// [generic.d.ts] +export declare function noop(): void; +/** + * A named mixin. + * @template T + * @typedef {T & {name: string}} MixinName + */ +export type MixinName = T & { + name: string; +}; +/** + * Identity. + * @template T + * @callback Identity + * @param {T} x + * @returns {T} + */ +export type Identity = (x: T) => T; +export declare function j(): void; +//// [templateOnHost.d.ts] +export declare function noop(): void; +/** + * Documents k, not a type. + * @template T + * @param {T} x + */ +export declare function k(x: T): T; +//// [standaloneImport.d.ts] +export declare function noop(): void; +/** + * Brings Point into scope. + * @import {Point} from "./typedef.js" + */ +import type { Point } from "./typedef.js"; +/** @type {Point} */ +export declare const p: Point; +//// [overload.d.ts] +export declare function noop(): void; +/** + * Takes a string. + * @overload + * @param {string} x + * @returns {string} + */ +export declare function l(x: string): string; diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.symbols b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.symbols index 1ed309df2d385..afd22c3867af3 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.symbols +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.symbols @@ -119,3 +119,72 @@ export function noop() {} export function i() {} >i : Symbol(i, Decl(preceded.js, 0, 25)) +=== generic.js === +export function noop() {} +>noop : Symbol(noop, Decl(generic.js, 0, 0)) + +/** + * A named mixin. + * @template T + * @typedef {T & {name: string}} MixinName + */ + +/** + * Identity. + * @template T + * @callback Identity + * @param {T} x + * @returns {T} + */ + +export function j() {} +>j : Symbol(j, Decl(generic.js, 0, 25)) + +=== templateOnHost.js === +export function noop() {} +>noop : Symbol(noop, Decl(templateOnHost.js, 0, 0)) + +/** + * Documents k, not a type. + * @template T + * @param {T} x + */ +export function k(x) { return x; } +>k : Symbol(k, Decl(templateOnHost.js, 0, 25)) +>x : Symbol(x, Decl(templateOnHost.js, 7, 18)) +>x : Symbol(x, Decl(templateOnHost.js, 7, 18)) + +=== standaloneImport.js === +export function noop() {} +>noop : Symbol(noop, Decl(standaloneImport.js, 0, 0)) + +/** + * Brings Point into scope. + * @import {Point} from "./typedef.js" + */ + +/** @type {Point} */ +export const p = { x: 0, y: 0 }; +>p : Symbol(p, Decl(standaloneImport.js, 8, 12)) +>x : Symbol(x, Decl(standaloneImport.js, 8, 18)) +>y : Symbol(y, Decl(standaloneImport.js, 8, 24)) + +=== overload.js === +export function noop() {} +>noop : Symbol(noop, Decl(overload.js, 0, 0)) + +/** + * Takes a string. + * @overload + * @param {string} x + * @returns {string} + */ + +/** + * @param {any} x + */ +export function l(x) { return x; } +>l : Symbol(l, Decl(overload.js, 4, 4), Decl(overload.js, 0, 25)) +>x : Symbol(x, Decl(overload.js, 12, 18)) +>x : Symbol(x, Decl(overload.js, 12, 18)) + diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.types b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.types index 3501d0948a4c4..8cdfc89a3c39d 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.types +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.types @@ -121,3 +121,75 @@ export function noop() {} export function i() {} >i : () => void +=== generic.js === +export function noop() {} +>noop : () => void + +/** + * A named mixin. + * @template T + * @typedef {T & {name: string}} MixinName + */ + +/** + * Identity. + * @template T + * @callback Identity + * @param {T} x + * @returns {T} + */ + +export function j() {} +>j : () => void + +=== templateOnHost.js === +export function noop() {} +>noop : () => void + +/** + * Documents k, not a type. + * @template T + * @param {T} x + */ +export function k(x) { return x; } +>k : (x: T) => T +>x : T +>x : T + +=== standaloneImport.js === +export function noop() {} +>noop : () => void + +/** + * Brings Point into scope. + * @import {Point} from "./typedef.js" + */ + +/** @type {Point} */ +export const p = { x: 0, y: 0 }; +>p : Point +>{ x: 0, y: 0 } : { x: number; y: number; } +>x : number +>0 : 0 +>y : number +>0 : 0 + +=== overload.js === +export function noop() {} +>noop : () => void + +/** + * Takes a string. + * @overload + * @param {string} x + * @returns {string} + */ + +/** + * @param {any} x + */ +export function l(x) { return x; } +>l : (x: string) => string +>x : any +>x : any + diff --git a/tsc/testdata/baselines/reference/conformance/jsdocTemplateTagDefault.js b/tsc/testdata/baselines/reference/conformance/jsdocTemplateTagDefault.js index 449020ebe88d0..4ff0cb25c8ac9 100644 --- a/tsc/testdata/baselines/reference/conformance/jsdocTemplateTagDefault.js +++ b/tsc/testdata/baselines/reference/conformance/jsdocTemplateTagDefault.js @@ -138,34 +138,34 @@ type A = [T]; /** @type {A} */ declare const aDefault2: A; /** @type {A} */ declare const aString: A; /** @type {A} */ declare const aNumber: A; -type B = [T, U]; -type C = [T]; -type D = [T]; -type E = [T, U]; -type G = [T, U]; /** * @template T * @template [U=T] - ok: default can reference earlier type parameter * @typedef {[T, U]} B */ +type B = [T, U]; /** * @template {string | number} [T] - error: default requires an `=type` * @typedef {[T]} C */ +type C = [T]; /** * @template {string | number} [T=] - error: default requires a `type` * @typedef {[T]} D */ +type D = [T]; /** * @template {string | number} [T=string] * @template U - error: Required type parameters cannot follow optional type parameters * @typedef {[T, U]} E */ +type E = [T, U]; /** * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. * @template [U=T] * @typedef {[T, U]} G */ +type G = [T, U]; /** * @template T * @template [U=T] - ok: default can reference earlier type parameter diff --git a/tsc/testdata/baselines/reference/conformance/jsdocVariadicInOverload.errors.txt b/tsc/testdata/baselines/reference/conformance/jsdocVariadicInOverload.errors.txt new file mode 100644 index 0000000000000..81fb17edbbd1a --- /dev/null +++ b/tsc/testdata/baselines/reference/conformance/jsdocVariadicInOverload.errors.txt @@ -0,0 +1,52 @@ +typeTagForMultipleVariableDeclarations.js(29,60): error TS8024: JSDoc '@param' tag has name 'value', but there is no parameter with that name. +typeTagForMultipleVariableDeclarations.js(31,26): error TS8024: JSDoc '@param' tag has name 'parameters', but there is no parameter with that name. + + +==== typeTagForMultipleVariableDeclarations.js (2 errors) ==== + // based on code from unifiedjs/unified + class Node {} + /** + * @template {Node | undefined} [ParseTree=undefined] + * Output of `parse` (optional). + * @template {Node | undefined} [HeadTree=undefined] + * Input for `run` (optional). + * @template {Node | undefined} [TailTree=undefined] + * Output for `run` (optional). + * @template {Node | undefined} [CompileTree=undefined] + * Input of `stringify` (optional). + * @template {string | undefined} [CompileResult=undefined] + * Output of `stringify` (optional). + */ + export class Processor { + /** + * @overload + * @param {string | null | undefined} [preset] + * @returns {Processor} + * + * @template {Array} [Parameters=[]] + * @template {Node | string | undefined} [Input=undefined] + * @template [Output=Input] + * @overload + * @param {number} plugin + * @param {...(Parameters | [boolean])} parameters + * @returns {Processor} + * + * @param {string | number | boolean | null | undefined} value + ~~~~~ +!!! error TS8024: JSDoc '@param' tag has name 'value', but there is no parameter with that name. + * Usable value. + * @param {...unknown} parameters + ~~~~~~~~~~ +!!! error TS8024: JSDoc '@param' tag has name 'parameters', but there is no parameter with that name. + * Parameters, when a plugin is given as a usable value. + * @returns {Processor} + * Current processor. + */ + use(value, ...parameters) { + return this; + } + } + var p = new Processor(); + var x = 1, y = 2, z = 3; + p.use(x, y, z); + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/conformance/overloadTag1.js b/tsc/testdata/baselines/reference/conformance/overloadTag1.js index 77851b8b1744b..fb8f7ab7b50a8 100644 --- a/tsc/testdata/baselines/reference/conformance/overloadTag1.js +++ b/tsc/testdata/baselines/reference/conformance/overloadTag1.js @@ -95,5 +95,16 @@ uncheckedInternally("zero", "one"); //// [overloadTag1.d.ts] export declare function overloaded(a: number, b: number): number; export declare function overloaded(a: string, b: boolean): string; +/** + * @overload + * @param {number} a + * @param {number} b + * @returns {number} + * + * @overload + * @param {string} a + * @param {boolean} b + * @returns {string} + */ export declare function uncheckedInternally(a: number, b: number): number; export declare function uncheckedInternally(a: string, b: boolean): string; diff --git a/tsc/testdata/baselines/reference/conformance/recursiveTypeReferences2.js b/tsc/testdata/baselines/reference/conformance/recursiveTypeReferences2.js index bd343ff7ced45..f62c131c959e5 100644 --- a/tsc/testdata/baselines/reference/conformance/recursiveTypeReferences2.js +++ b/tsc/testdata/baselines/reference/conformance/recursiveTypeReferences2.js @@ -64,17 +64,6 @@ type JsonRecord = { }; /** @typedef {boolean | number | string | null | JsonRecord | JsonArray | readonly []} Json */ type Json = boolean | number | string | null | JsonRecord | JsonArray | readonly []; -type XMLObject = { - $A: { [K in keyof T]?: XMLObject[]; }; - $O: { [K in keyof T]?: { - $$?: Record; - } & (T[K] extends string ? { - $: string; - } : XMLObject); }; - $$?: Record; -} & { - [K in keyof T]?: (T[K] extends string ? string : XMLObject); -}; /** * @template T * @typedef {{ @@ -93,6 +82,17 @@ type XMLObject = { : XMLObject ) }} XMLObject */ +type XMLObject = { + $A: { [K in keyof T]?: XMLObject[]; }; + $O: { [K in keyof T]?: { + $$?: Record; + } & (T[K] extends string ? { + $: string; + } : XMLObject); }; + $$?: Record; +} & { + [K in keyof T]?: (T[K] extends string ? string : XMLObject); +}; /** @type {XMLObject<{foo:string}>} */ declare const p: XMLObject<{ foo: string; diff --git a/tsc/testdata/baselines/reference/conformance/templateInsideCallback.js b/tsc/testdata/baselines/reference/conformance/templateInsideCallback.js index 06756655abeb9..7d467b29c1e7e 100644 --- a/tsc/testdata/baselines/reference/conformance/templateInsideCallback.js +++ b/tsc/testdata/baselines/reference/conformance/templateInsideCallback.js @@ -148,5 +148,19 @@ type Nested = { noooooo: string; }; }; +/** + * @overload + * @template T + * @template U + * @param {T[]} array + * @param {(x: T) => U[]} iterable + * @returns {U[]} + */ declare function flatMap(array: T[], iterable: (x: T) => U[]): U[]; +/** + * @overload + * @template T + * @param {T[][]} array + * @returns {T[]} + */ declare function flatMap(array: T[][]): T[]; diff --git a/tsc/testdata/baselines/reference/fourslash/signatureHelp/quickInfoJsDocTags13.baseline b/tsc/testdata/baselines/reference/fourslash/signatureHelp/quickInfoJsDocTags13.baseline index e67cb9cd3e7bb..4834569af551d 100644 --- a/tsc/testdata/baselines/reference/fourslash/signatureHelp/quickInfoJsDocTags13.baseline +++ b/tsc/testdata/baselines/reference/fourslash/signatureHelp/quickInfoJsDocTags13.baseline @@ -24,11 +24,13 @@ // ^ // | ---------------------------------------------------------------------- // | f(**a: number**): void +// | First overload // | ---------------------------------------------------------------------- // f(""); // ^ // | ---------------------------------------------------------------------- // | f(**a: string**): void +// | Second overload // | ---------------------------------------------------------------------- [ { @@ -45,6 +47,10 @@ "signatures": [ { "label": "f(a: number): void", + "documentation": { + "kind": "markdown", + "value": "First overload" + }, "parameters": [ { "label": "a: number" @@ -54,6 +60,10 @@ }, { "label": "f(a: string): void", + "documentation": { + "kind": "markdown", + "value": "Second overload" + }, "parameters": [ { "label": "a: string" @@ -79,6 +89,10 @@ "signatures": [ { "label": "f(a: number): void", + "documentation": { + "kind": "markdown", + "value": "First overload" + }, "parameters": [ { "label": "a: number" @@ -88,6 +102,10 @@ }, { "label": "f(a: string): void", + "documentation": { + "kind": "markdown", + "value": "Second overload" + }, "parameters": [ { "label": "a: string" diff --git a/tsc/testdata/baselines/reference/fourslash/signatureHelp/quickInfoJsDocTags13VS.baseline b/tsc/testdata/baselines/reference/fourslash/signatureHelp/quickInfoJsDocTags13VS.baseline index e84f3ed94b1ec..a8c1a36f1635e 100644 --- a/tsc/testdata/baselines/reference/fourslash/signatureHelp/quickInfoJsDocTags13VS.baseline +++ b/tsc/testdata/baselines/reference/fourslash/signatureHelp/quickInfoJsDocTags13VS.baseline @@ -24,11 +24,13 @@ // ^ // | ---------------------------------------------------------------------- // | f(**a: number**): void +// | First overload // | ---------------------------------------------------------------------- // f(""); // ^ // | ---------------------------------------------------------------------- // | f(**a: string**): void +// | Second overload // | ---------------------------------------------------------------------- [ { @@ -45,6 +47,10 @@ "signatures": [ { "label": "f(a: number): void", + "documentation": { + "kind": "markdown", + "value": "First overload" + }, "parameters": [ { "label": "a: number" @@ -104,6 +110,10 @@ }, { "label": "f(a: string): void", + "documentation": { + "kind": "markdown", + "value": "Second overload" + }, "parameters": [ { "label": "a: string" @@ -179,6 +189,10 @@ "signatures": [ { "label": "f(a: number): void", + "documentation": { + "kind": "markdown", + "value": "First overload" + }, "parameters": [ { "label": "a: number" @@ -238,6 +252,10 @@ }, { "label": "f(a: string): void", + "documentation": { + "kind": "markdown", + "value": "Second overload" + }, "parameters": [ { "label": "a: string" diff --git a/tsc/testdata/baselines/reference/tsbuild/javascriptProjectEmit/loads-js-based-projects-and-emits-them-correctly.js b/tsc/testdata/baselines/reference/tsbuild/javascriptProjectEmit/loads-js-based-projects-and-emits-them-correctly.js index 25cd8eb4af983..d0a7161e4784e 100644 --- a/tsc/testdata/baselines/reference/tsbuild/javascriptProjectEmit/loads-js-based-projects-and-emits-them-correctly.js +++ b/tsc/testdata/baselines/reference/tsbuild/javascriptProjectEmit/loads-js-based-projects-and-emits-them-correctly.js @@ -153,12 +153,12 @@ interface Symbol { } declare const console: { log(msg: any): void; }; //// [/home/src/workspaces/lib/common/nominal.d.ts] *new* +declare const _exports: {}; +export = _exports; /** * @template T, Name * @typedef {T & {[Symbol.species]: Name}} Nominal */ -declare const _exports: {}; -export = _exports; export type Nominal = T & {}; //// [/home/src/workspaces/lib/common/nominal.js] *new* @@ -170,7 +170,7 @@ export type Nominal = T & {}; module.exports = {}; //// [/home/src/workspaces/lib/common/tsconfig.tsbuildinfo] *new* -{"version":"FakeTSVersion","root":[2],"fileNames":["lib.es2025.full.d.ts","../../solution/common/nominal.js"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"a19075dfba5b2d593b761ed8d8cd526f-/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\nmodule.exports = {};","signature":"81ac3a6e47fc4be420cd544e175d838d-/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\ndeclare const _exports: {};\nexport = _exports;\nexport type Nominal = T & {};\n","impliedNodeFormat":1}],"options":{"allowJs":true,"checkJs":true,"composite":true,"declaration":true,"outDir":"..","rootDir":"../../solution","skipLibCheck":true},"semanticDiagnosticsPerFile":[[2,[{"pos":51,"end":58,"code":2339,"category":1,"messageKey":"Property_0_does_not_exist_on_type_1_2339","messageArgs":["species","SymbolConstructor"]}]]],"latestChangedDtsFile":"./nominal.d.ts"} +{"version":"FakeTSVersion","root":[2],"fileNames":["lib.es2025.full.d.ts","../../solution/common/nominal.js"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"a19075dfba5b2d593b761ed8d8cd526f-/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\nmodule.exports = {};","signature":"77e32cd27288eae696416a61327db719-declare const _exports: {};\nexport = _exports;\n/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\nexport type Nominal = T & {};\n","impliedNodeFormat":1}],"options":{"allowJs":true,"checkJs":true,"composite":true,"declaration":true,"outDir":"..","rootDir":"../../solution","skipLibCheck":true},"semanticDiagnosticsPerFile":[[2,[{"pos":51,"end":58,"code":2339,"category":1,"messageKey":"Property_0_does_not_exist_on_type_1_2339","messageArgs":["species","SymbolConstructor"]}]]],"latestChangedDtsFile":"./nominal.d.ts"} //// [/home/src/workspaces/lib/common/tsconfig.tsbuildinfo.readable.baseline.txt] *new* { "version": "FakeTSVersion", @@ -202,11 +202,11 @@ module.exports = {}; { "fileName": "../../solution/common/nominal.js", "version": "a19075dfba5b2d593b761ed8d8cd526f-/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\nmodule.exports = {};", - "signature": "81ac3a6e47fc4be420cd544e175d838d-/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\ndeclare const _exports: {};\nexport = _exports;\nexport type Nominal = T & {};\n", + "signature": "77e32cd27288eae696416a61327db719-declare const _exports: {};\nexport = _exports;\n/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\nexport type Nominal = T & {};\n", "impliedNodeFormat": "CommonJS", "original": { "version": "a19075dfba5b2d593b761ed8d8cd526f-/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\nmodule.exports = {};", - "signature": "81ac3a6e47fc4be420cd544e175d838d-/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\ndeclare const _exports: {};\nexport = _exports;\nexport type Nominal = T & {};\n", + "signature": "77e32cd27288eae696416a61327db719-declare const _exports: {};\nexport = _exports;\n/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\nexport type Nominal = T & {};\n", "impliedNodeFormat": 1 } } @@ -265,7 +265,7 @@ export function getVar() { } //// [/home/src/workspaces/lib/sub-project-2/tsconfig.tsbuildinfo] *new* -{"version":"FakeTSVersion","root":[4],"fileNames":["lib.es2025.full.d.ts","../common/nominal.d.ts","../sub-project/index.d.ts","../../solution/sub-project-2/index.js"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},"81ac3a6e47fc4be420cd544e175d838d-/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\ndeclare const _exports: {};\nexport = _exports;\nexport type Nominal = T & {};\n","5fa17a470c0fbdd3d822f5e7ff2044b0-import { Nominal } from '../common/nominal';\n/**\n * @typedef {Nominal} MyNominal\n */ \nexport type MyNominal = Nominal;\n",{"version":"db2a90e082fd17d65127bda69975a727-import { MyNominal } from '../sub-project/index';\n\nconst variable = {\n key: /** @type {MyNominal} */('value'),\n};\n\n/**\n * @return {keyof typeof variable}\n */\nexport function getVar() {\n return 'key';\n}","signature":"0620ca4a9fe7036c93bd1594ffccf8eb-import { MyNominal } from '../sub-project/index';\ndeclare const variable: {\n key: MyNominal;\n};\n/**\n * @return {keyof typeof variable}\n */\nexport declare function getVar(): keyof typeof variable;\nexport {};\n","impliedNodeFormat":1}],"fileIdsList":[[2],[3]],"options":{"allowJs":true,"checkJs":true,"composite":true,"declaration":true,"outDir":"..","rootDir":"../../solution","skipLibCheck":true},"referencedMap":[[3,1],[4,2]],"semanticDiagnosticsPerFile":[[4,[{"pos":9,"end":18,"code":18042,"category":1,"messageKey":"_0_is_a_type_and_cannot_be_imported_in_JavaScript_files_Use_1_in_a_JSDoc_type_annotation_18042","messageArgs":["MyNominal","import(\"../sub-project/index\").MyNominal"]}]]],"latestChangedDtsFile":"./index.d.ts"} +{"version":"FakeTSVersion","root":[4],"fileNames":["lib.es2025.full.d.ts","../common/nominal.d.ts","../sub-project/index.d.ts","../../solution/sub-project-2/index.js"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},"77e32cd27288eae696416a61327db719-declare const _exports: {};\nexport = _exports;\n/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\nexport type Nominal = T & {};\n","5fa17a470c0fbdd3d822f5e7ff2044b0-import { Nominal } from '../common/nominal';\n/**\n * @typedef {Nominal} MyNominal\n */ \nexport type MyNominal = Nominal;\n",{"version":"db2a90e082fd17d65127bda69975a727-import { MyNominal } from '../sub-project/index';\n\nconst variable = {\n key: /** @type {MyNominal} */('value'),\n};\n\n/**\n * @return {keyof typeof variable}\n */\nexport function getVar() {\n return 'key';\n}","signature":"0620ca4a9fe7036c93bd1594ffccf8eb-import { MyNominal } from '../sub-project/index';\ndeclare const variable: {\n key: MyNominal;\n};\n/**\n * @return {keyof typeof variable}\n */\nexport declare function getVar(): keyof typeof variable;\nexport {};\n","impliedNodeFormat":1}],"fileIdsList":[[2],[3]],"options":{"allowJs":true,"checkJs":true,"composite":true,"declaration":true,"outDir":"..","rootDir":"../../solution","skipLibCheck":true},"referencedMap":[[3,1],[4,2]],"semanticDiagnosticsPerFile":[[4,[{"pos":9,"end":18,"code":18042,"category":1,"messageKey":"_0_is_a_type_and_cannot_be_imported_in_JavaScript_files_Use_1_in_a_JSDoc_type_annotation_18042","messageArgs":["MyNominal","import(\"../sub-project/index\").MyNominal"]}]]],"latestChangedDtsFile":"./index.d.ts"} //// [/home/src/workspaces/lib/sub-project-2/tsconfig.tsbuildinfo.readable.baseline.txt] *new* { "version": "FakeTSVersion", @@ -298,8 +298,8 @@ export function getVar() { }, { "fileName": "../common/nominal.d.ts", - "version": "81ac3a6e47fc4be420cd544e175d838d-/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\ndeclare const _exports: {};\nexport = _exports;\nexport type Nominal = T & {};\n", - "signature": "81ac3a6e47fc4be420cd544e175d838d-/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\ndeclare const _exports: {};\nexport = _exports;\nexport type Nominal = T & {};\n", + "version": "77e32cd27288eae696416a61327db719-declare const _exports: {};\nexport = _exports;\n/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\nexport type Nominal = T & {};\n", + "signature": "77e32cd27288eae696416a61327db719-declare const _exports: {};\nexport = _exports;\n/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\nexport type Nominal = T & {};\n", "impliedNodeFormat": "CommonJS" }, { @@ -380,7 +380,7 @@ import { Nominal } from '../common/nominal'; */ //// [/home/src/workspaces/lib/sub-project/tsconfig.tsbuildinfo] *new* -{"version":"FakeTSVersion","root":[3],"fileNames":["lib.es2025.full.d.ts","../common/nominal.d.ts","../../solution/sub-project/index.js"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},"81ac3a6e47fc4be420cd544e175d838d-/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\ndeclare const _exports: {};\nexport = _exports;\nexport type Nominal = T & {};\n",{"version":"00b7836eaf1e026f7764b7be6efcc8f5-import { Nominal } from '../common/nominal';\n\n/**\n * @typedef {Nominal} MyNominal\n */","signature":"5fa17a470c0fbdd3d822f5e7ff2044b0-import { Nominal } from '../common/nominal';\n/**\n * @typedef {Nominal} MyNominal\n */ \nexport type MyNominal = Nominal;\n","impliedNodeFormat":1}],"fileIdsList":[[2]],"options":{"allowJs":true,"checkJs":true,"composite":true,"declaration":true,"outDir":"..","rootDir":"../../solution","skipLibCheck":true},"referencedMap":[[3,1]],"semanticDiagnosticsPerFile":[[3,[{"pos":9,"end":16,"code":18042,"category":1,"messageKey":"_0_is_a_type_and_cannot_be_imported_in_JavaScript_files_Use_1_in_a_JSDoc_type_annotation_18042","messageArgs":["Nominal","import(\"../common/nominal\").Nominal"]}]]],"latestChangedDtsFile":"./index.d.ts"} +{"version":"FakeTSVersion","root":[3],"fileNames":["lib.es2025.full.d.ts","../common/nominal.d.ts","../../solution/sub-project/index.js"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},"77e32cd27288eae696416a61327db719-declare const _exports: {};\nexport = _exports;\n/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\nexport type Nominal = T & {};\n",{"version":"00b7836eaf1e026f7764b7be6efcc8f5-import { Nominal } from '../common/nominal';\n\n/**\n * @typedef {Nominal} MyNominal\n */","signature":"5fa17a470c0fbdd3d822f5e7ff2044b0-import { Nominal } from '../common/nominal';\n/**\n * @typedef {Nominal} MyNominal\n */ \nexport type MyNominal = Nominal;\n","impliedNodeFormat":1}],"fileIdsList":[[2]],"options":{"allowJs":true,"checkJs":true,"composite":true,"declaration":true,"outDir":"..","rootDir":"../../solution","skipLibCheck":true},"referencedMap":[[3,1]],"semanticDiagnosticsPerFile":[[3,[{"pos":9,"end":16,"code":18042,"category":1,"messageKey":"_0_is_a_type_and_cannot_be_imported_in_JavaScript_files_Use_1_in_a_JSDoc_type_annotation_18042","messageArgs":["Nominal","import(\"../common/nominal\").Nominal"]}]]],"latestChangedDtsFile":"./index.d.ts"} //// [/home/src/workspaces/lib/sub-project/tsconfig.tsbuildinfo.readable.baseline.txt] *new* { "version": "FakeTSVersion", @@ -412,8 +412,8 @@ import { Nominal } from '../common/nominal'; }, { "fileName": "../common/nominal.d.ts", - "version": "81ac3a6e47fc4be420cd544e175d838d-/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\ndeclare const _exports: {};\nexport = _exports;\nexport type Nominal = T & {};\n", - "signature": "81ac3a6e47fc4be420cd544e175d838d-/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\ndeclare const _exports: {};\nexport = _exports;\nexport type Nominal = T & {};\n", + "version": "77e32cd27288eae696416a61327db719-declare const _exports: {};\nexport = _exports;\n/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\nexport type Nominal = T & {};\n", + "signature": "77e32cd27288eae696416a61327db719-declare const _exports: {};\nexport = _exports;\n/**\n * @template T, Name\n * @typedef {T & {[Symbol.species]: Name}} Nominal\n */\nexport type Nominal = T & {};\n", "impliedNodeFormat": "CommonJS" }, { diff --git a/tsc/testdata/tests/cases/conformance/jsdoc/declarations/jsDeclarationsTypedefCommentPlacement.ts b/tsc/testdata/tests/cases/conformance/jsdoc/declarations/jsDeclarationsTypedefCommentPlacement.ts index 672b0582454c6..fce63dfd64a81 100644 --- a/tsc/testdata/tests/cases/conformance/jsdoc/declarations/jsDeclarationsTypedefCommentPlacement.ts +++ b/tsc/testdata/tests/cases/conformance/jsdoc/declarations/jsDeclarationsTypedefCommentPlacement.ts @@ -98,3 +98,62 @@ export function noop() {} */ export function i() {} + +// @filename: generic.js + +export function noop() {} + +/** + * A named mixin. + * @template T + * @typedef {T & {name: string}} MixinName + */ + +/** + * Identity. + * @template T + * @callback Identity + * @param {T} x + * @returns {T} + */ + +export function j() {} + +// @filename: templateOnHost.js + +export function noop() {} + +/** + * Documents k, not a type. + * @template T + * @param {T} x + */ +export function k(x) { return x; } + +// @filename: standaloneImport.js + +export function noop() {} + +/** + * Brings Point into scope. + * @import {Point} from "./typedef.js" + */ + +/** @type {Point} */ +export const p = { x: 0, y: 0 }; + +// @filename: overload.js + +export function noop() {} + +/** + * Takes a string. + * @overload + * @param {string} x + * @returns {string} + */ + +/** + * @param {any} x + */ +export function l(x) { return x; } From ac75138c4c71da334951d4a5c08f39dd2accad4a Mon Sep 17 00:00:00 2001 From: Eugene Kalinin Date: Sat, 5 Sep 2026 23:10:41 +0300 Subject: [PATCH 3/3] fix(declarations): decide comment ownership by what a JSDoc tag documents Review follow-up. Registering the containing JSDoc on a reparsed overload signature fed the whole block to the checker as that signature's JSDoc, so checkUnmatchedJSDocParameters validated the implementation's own `@param` tags against it and reported TS8024 for parameters that do exist. Overload documentation was never emitted before this branch, so drop the registration rather than narrow it; scoping a comment to a single `@overload` tag is a change of its own. Invert the tag predicate. Instead of listing the tags that may be claimed, a comment is claimed when it declares something and carries no tag that documents another node - the ones the parser applies to the host, per reparseHosted, plus `@overload`. Everything left over, a description or `@see` or `@example`, describes what the comment declares, so a `@typedef` beside a `@see` no longer leaves its comment behind. Claim over the statements as they enter the late-painted pass rather than over its output. A comment whose declaration is elided is then claimed by nothing and drops with it, instead of being swept onto the next declaration. --- tsc/internal/parser/reparser.go | 5 +- tsc/internal/printer/emitcontext.go | 14 +++- .../transformers/declarations/transform.go | 66 ++++++++++++++----- .../compiler/jsDocGenericOverloads.errors.txt | 41 ------------ .../compiler/jsFileFunctionOverloads.js | 15 ----- .../reference/conformance/importDeferJsdoc.js | 3 - .../conformance/importTag15(module=es2015).js | 1 - .../conformance/importTag15(module=esnext).js | 1 - .../jsDeclarationsTypedefCommentPlacement.js | 58 ++++++++++++++-- ...eclarationsTypedefCommentPlacement.symbols | 27 ++++++++ ...sDeclarationsTypedefCommentPlacement.types | 27 ++++++++ .../jsdocVariadicInOverload.errors.txt | 52 --------------- .../reference/conformance/linkTagEmit1.js | 2 +- .../reference/conformance/overloadTag1.js | 11 ---- .../conformance/templateInsideCallback.js | 14 ---- .../quickInfoJsDocTags13.baseline | 18 ----- .../quickInfoJsDocTags13VS.baseline | 18 ----- .../jsDeclarationsTypedefCommentPlacement.ts | 25 +++++++ 18 files changed, 194 insertions(+), 204 deletions(-) delete mode 100644 tsc/testdata/baselines/reference/compiler/jsDocGenericOverloads.errors.txt delete mode 100644 tsc/testdata/baselines/reference/conformance/jsdocVariadicInOverload.errors.txt diff --git a/tsc/internal/parser/reparser.go b/tsc/internal/parser/reparser.go index 493f8d0fee126..7e548a1a068f2 100644 --- a/tsc/internal/parser/reparser.go +++ b/tsc/internal/parser/reparser.go @@ -140,10 +140,7 @@ func (p *Parser) reparseUnhosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Nod case ast.KindJSDocOverloadTag: // Create overload signatures only for function, method, and constructor declarations outside object literals if (ast.IsFunctionDeclaration(parent) || ast.IsMethodDeclaration(parent) || ast.IsConstructorDeclaration(parent)) && p.parsingContexts&(1< { - /* ... omitted for brevity ... */ return document.createElement(t) - } - - /** - * @template {keyof HTMLElementTagNameMap} T - * @param {T}t - * @param {NodeList|HTMLCollection=}c - ~ -!!! error TS8024: JSDoc '@param' tag has name 'c', but there is no parameter with that name. - * - * @overload - * @param {T}t - * @return {HTMLElementTagNameMap[T]} - * - * @overload - * @param {T}t - * @param {NodeList|HTMLCollection}c - * @return {HTMLElementTagNameMap[T]} - */ - function createElementF(t, c) { - /* ... omitted for brevity ... */ return document.createElement(t) - } - \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/jsFileFunctionOverloads.js b/tsc/testdata/baselines/reference/compiler/jsFileFunctionOverloads.js index 5ca184c33b9ca..72fee946e753c 100644 --- a/tsc/testdata/baselines/reference/compiler/jsFileFunctionOverloads.js +++ b/tsc/testdata/baselines/reference/compiler/jsFileFunctionOverloads.js @@ -120,23 +120,8 @@ function flatMap(array, iterable = identity) { //// [jsFileFunctionOverloads.d.ts] -/** - * @overload - * @param {number} x - * @returns {'number'} - */ declare function getTypeName(x: number): 'number'; -/** - * @overload - * @param {string} x - * @returns {'string'} - */ declare function getTypeName(x: string): 'string'; -/** - * @overload - * @param {boolean} x - * @returns {'boolean'} - */ declare function getTypeName(x: boolean): 'boolean'; /** * @template T diff --git a/tsc/testdata/baselines/reference/conformance/importDeferJsdoc.js b/tsc/testdata/baselines/reference/conformance/importDeferJsdoc.js index 13607f52d3319..dc7f8bc6a7a81 100644 --- a/tsc/testdata/baselines/reference/conformance/importDeferJsdoc.js +++ b/tsc/testdata/baselines/reference/conformance/importDeferJsdoc.js @@ -19,9 +19,6 @@ let a = 2; //// [types.d.ts] export type X = 1; //// [foo.d.ts] -/** - * @import defer * as ns from "./types" - */ /** * @type { ns.X } */ diff --git a/tsc/testdata/baselines/reference/conformance/importTag15(module=es2015).js b/tsc/testdata/baselines/reference/conformance/importTag15(module=es2015).js index 099fec4f4e71b..8d502927e436b 100644 --- a/tsc/testdata/baselines/reference/conformance/importTag15(module=es2015).js +++ b/tsc/testdata/baselines/reference/conformance/importTag15(module=es2015).js @@ -19,6 +19,5 @@ export interface I { //// [1.d.ts] /** @import { I } from './0' with { type: "json" } */ import type { I } from './0' with { type: "json" }; -/** @import * as foo from './0' with { type: "json" } */ /** @param {I} a */ declare function f(a: I): void; diff --git a/tsc/testdata/baselines/reference/conformance/importTag15(module=esnext).js b/tsc/testdata/baselines/reference/conformance/importTag15(module=esnext).js index 099fec4f4e71b..8d502927e436b 100644 --- a/tsc/testdata/baselines/reference/conformance/importTag15(module=esnext).js +++ b/tsc/testdata/baselines/reference/conformance/importTag15(module=esnext).js @@ -19,6 +19,5 @@ export interface I { //// [1.d.ts] /** @import { I } from './0' with { type: "json" } */ import type { I } from './0' with { type: "json" }; -/** @import * as foo from './0' with { type: "json" } */ /** @param {I} a */ declare function f(a: I): void; diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.js b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.js index d8704513623a4..9cc63f2e0e111 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.js +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.js @@ -144,6 +144,29 @@ export function noop() {} */ export function l(x) { return x; } +//// [documentationTags.js] +export function noop() {} + +/** + * Prose describing S. + * @typedef {number} S + * @see noop + * @example + * const s = 1; + */ + +export function m() {} + +//// [elidedImport.js] +export function noop() {} + +/** + * Nothing in the output uses this import. + * @import {Point} from "./typedef.js" + */ + +export function n() {} + //// [typedef.js] export function noop() { } @@ -258,6 +281,23 @@ export function noop() { } * @param {any} x */ export function l(x) { return x; } +//// [documentationTags.js] +export function noop() { } +/** + * Prose describing S. + * @typedef {number} S + * @see noop + * @example + * const s = 1; + */ +export function m() { } +//// [elidedImport.js] +export function noop() { } +/** + * Nothing in the output uses this import. + * @import {Point} from "./typedef.js" + */ +export function n() { } //// [typedef.d.ts] @@ -375,10 +415,18 @@ import type { Point } from "./typedef.js"; export declare const p: Point; //// [overload.d.ts] export declare function noop(): void; +export declare function l(x: string): string; +//// [documentationTags.d.ts] +export declare function noop(): void; /** - * Takes a string. - * @overload - * @param {string} x - * @returns {string} + * Prose describing S. + * @typedef {number} S + * @see noop + * @example + * const s = 1; */ -export declare function l(x: string): string; +export type S = number; +export declare function m(): void; +//// [elidedImport.d.ts] +export declare function noop(): void; +export declare function n(): void; diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.symbols b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.symbols index afd22c3867af3..bfa218b64a58c 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.symbols +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.symbols @@ -188,3 +188,30 @@ export function l(x) { return x; } >x : Symbol(x, Decl(overload.js, 12, 18)) >x : Symbol(x, Decl(overload.js, 12, 18)) +=== documentationTags.js === +export function noop() {} +>noop : Symbol(noop, Decl(documentationTags.js, 0, 0)) + +/** + * Prose describing S. + * @typedef {number} S + * @see noop + * @example + * const s = 1; + */ + +export function m() {} +>m : Symbol(m, Decl(documentationTags.js, 0, 25)) + +=== elidedImport.js === +export function noop() {} +>noop : Symbol(noop, Decl(elidedImport.js, 0, 0)) + +/** + * Nothing in the output uses this import. + * @import {Point} from "./typedef.js" + */ + +export function n() {} +>n : Symbol(n, Decl(elidedImport.js, 0, 25)) + diff --git a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.types b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.types index 8cdfc89a3c39d..94421060806af 100644 --- a/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.types +++ b/tsc/testdata/baselines/reference/conformance/jsDeclarationsTypedefCommentPlacement.types @@ -193,3 +193,30 @@ export function l(x) { return x; } >x : any >x : any +=== documentationTags.js === +export function noop() {} +>noop : () => void + +/** + * Prose describing S. + * @typedef {number} S + * @see noop + * @example + * const s = 1; + */ + +export function m() {} +>m : () => void + +=== elidedImport.js === +export function noop() {} +>noop : () => void + +/** + * Nothing in the output uses this import. + * @import {Point} from "./typedef.js" + */ + +export function n() {} +>n : () => void + diff --git a/tsc/testdata/baselines/reference/conformance/jsdocVariadicInOverload.errors.txt b/tsc/testdata/baselines/reference/conformance/jsdocVariadicInOverload.errors.txt deleted file mode 100644 index 81fb17edbbd1a..0000000000000 --- a/tsc/testdata/baselines/reference/conformance/jsdocVariadicInOverload.errors.txt +++ /dev/null @@ -1,52 +0,0 @@ -typeTagForMultipleVariableDeclarations.js(29,60): error TS8024: JSDoc '@param' tag has name 'value', but there is no parameter with that name. -typeTagForMultipleVariableDeclarations.js(31,26): error TS8024: JSDoc '@param' tag has name 'parameters', but there is no parameter with that name. - - -==== typeTagForMultipleVariableDeclarations.js (2 errors) ==== - // based on code from unifiedjs/unified - class Node {} - /** - * @template {Node | undefined} [ParseTree=undefined] - * Output of `parse` (optional). - * @template {Node | undefined} [HeadTree=undefined] - * Input for `run` (optional). - * @template {Node | undefined} [TailTree=undefined] - * Output for `run` (optional). - * @template {Node | undefined} [CompileTree=undefined] - * Input of `stringify` (optional). - * @template {string | undefined} [CompileResult=undefined] - * Output of `stringify` (optional). - */ - export class Processor { - /** - * @overload - * @param {string | null | undefined} [preset] - * @returns {Processor} - * - * @template {Array} [Parameters=[]] - * @template {Node | string | undefined} [Input=undefined] - * @template [Output=Input] - * @overload - * @param {number} plugin - * @param {...(Parameters | [boolean])} parameters - * @returns {Processor} - * - * @param {string | number | boolean | null | undefined} value - ~~~~~ -!!! error TS8024: JSDoc '@param' tag has name 'value', but there is no parameter with that name. - * Usable value. - * @param {...unknown} parameters - ~~~~~~~~~~ -!!! error TS8024: JSDoc '@param' tag has name 'parameters', but there is no parameter with that name. - * Parameters, when a plugin is given as a usable value. - * @returns {Processor} - * Current processor. - */ - use(value, ...parameters) { - return this; - } - } - var p = new Processor(); - var x = 1, y = 2, z = 3; - p.use(x, y, z); - \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/conformance/linkTagEmit1.js b/tsc/testdata/baselines/reference/conformance/linkTagEmit1.js index b5c4f8f8d3c04..09da3929d9913 100644 --- a/tsc/testdata/baselines/reference/conformance/linkTagEmit1.js +++ b/tsc/testdata/baselines/reference/conformance/linkTagEmit1.js @@ -65,8 +65,8 @@ type D1 = { */ m: 1; }; -type Z = number; /** @typedef {number} Z @see N {@link N} */ +type Z = number; /** * @param {number} integer {@link Z} */ diff --git a/tsc/testdata/baselines/reference/conformance/overloadTag1.js b/tsc/testdata/baselines/reference/conformance/overloadTag1.js index fb8f7ab7b50a8..77851b8b1744b 100644 --- a/tsc/testdata/baselines/reference/conformance/overloadTag1.js +++ b/tsc/testdata/baselines/reference/conformance/overloadTag1.js @@ -95,16 +95,5 @@ uncheckedInternally("zero", "one"); //// [overloadTag1.d.ts] export declare function overloaded(a: number, b: number): number; export declare function overloaded(a: string, b: boolean): string; -/** - * @overload - * @param {number} a - * @param {number} b - * @returns {number} - * - * @overload - * @param {string} a - * @param {boolean} b - * @returns {string} - */ export declare function uncheckedInternally(a: number, b: number): number; export declare function uncheckedInternally(a: string, b: boolean): string; diff --git a/tsc/testdata/baselines/reference/conformance/templateInsideCallback.js b/tsc/testdata/baselines/reference/conformance/templateInsideCallback.js index 7d467b29c1e7e..06756655abeb9 100644 --- a/tsc/testdata/baselines/reference/conformance/templateInsideCallback.js +++ b/tsc/testdata/baselines/reference/conformance/templateInsideCallback.js @@ -148,19 +148,5 @@ type Nested = { noooooo: string; }; }; -/** - * @overload - * @template T - * @template U - * @param {T[]} array - * @param {(x: T) => U[]} iterable - * @returns {U[]} - */ declare function flatMap(array: T[], iterable: (x: T) => U[]): U[]; -/** - * @overload - * @template T - * @param {T[][]} array - * @returns {T[]} - */ declare function flatMap(array: T[][]): T[]; diff --git a/tsc/testdata/baselines/reference/fourslash/signatureHelp/quickInfoJsDocTags13.baseline b/tsc/testdata/baselines/reference/fourslash/signatureHelp/quickInfoJsDocTags13.baseline index 4834569af551d..e67cb9cd3e7bb 100644 --- a/tsc/testdata/baselines/reference/fourslash/signatureHelp/quickInfoJsDocTags13.baseline +++ b/tsc/testdata/baselines/reference/fourslash/signatureHelp/quickInfoJsDocTags13.baseline @@ -24,13 +24,11 @@ // ^ // | ---------------------------------------------------------------------- // | f(**a: number**): void -// | First overload // | ---------------------------------------------------------------------- // f(""); // ^ // | ---------------------------------------------------------------------- // | f(**a: string**): void -// | Second overload // | ---------------------------------------------------------------------- [ { @@ -47,10 +45,6 @@ "signatures": [ { "label": "f(a: number): void", - "documentation": { - "kind": "markdown", - "value": "First overload" - }, "parameters": [ { "label": "a: number" @@ -60,10 +54,6 @@ }, { "label": "f(a: string): void", - "documentation": { - "kind": "markdown", - "value": "Second overload" - }, "parameters": [ { "label": "a: string" @@ -89,10 +79,6 @@ "signatures": [ { "label": "f(a: number): void", - "documentation": { - "kind": "markdown", - "value": "First overload" - }, "parameters": [ { "label": "a: number" @@ -102,10 +88,6 @@ }, { "label": "f(a: string): void", - "documentation": { - "kind": "markdown", - "value": "Second overload" - }, "parameters": [ { "label": "a: string" diff --git a/tsc/testdata/baselines/reference/fourslash/signatureHelp/quickInfoJsDocTags13VS.baseline b/tsc/testdata/baselines/reference/fourslash/signatureHelp/quickInfoJsDocTags13VS.baseline index a8c1a36f1635e..e84f3ed94b1ec 100644 --- a/tsc/testdata/baselines/reference/fourslash/signatureHelp/quickInfoJsDocTags13VS.baseline +++ b/tsc/testdata/baselines/reference/fourslash/signatureHelp/quickInfoJsDocTags13VS.baseline @@ -24,13 +24,11 @@ // ^ // | ---------------------------------------------------------------------- // | f(**a: number**): void -// | First overload // | ---------------------------------------------------------------------- // f(""); // ^ // | ---------------------------------------------------------------------- // | f(**a: string**): void -// | Second overload // | ---------------------------------------------------------------------- [ { @@ -47,10 +45,6 @@ "signatures": [ { "label": "f(a: number): void", - "documentation": { - "kind": "markdown", - "value": "First overload" - }, "parameters": [ { "label": "a: number" @@ -110,10 +104,6 @@ }, { "label": "f(a: string): void", - "documentation": { - "kind": "markdown", - "value": "Second overload" - }, "parameters": [ { "label": "a: string" @@ -189,10 +179,6 @@ "signatures": [ { "label": "f(a: number): void", - "documentation": { - "kind": "markdown", - "value": "First overload" - }, "parameters": [ { "label": "a: number" @@ -252,10 +238,6 @@ }, { "label": "f(a: string): void", - "documentation": { - "kind": "markdown", - "value": "Second overload" - }, "parameters": [ { "label": "a: string" diff --git a/tsc/testdata/tests/cases/conformance/jsdoc/declarations/jsDeclarationsTypedefCommentPlacement.ts b/tsc/testdata/tests/cases/conformance/jsdoc/declarations/jsDeclarationsTypedefCommentPlacement.ts index fce63dfd64a81..e9d394dd1363e 100644 --- a/tsc/testdata/tests/cases/conformance/jsdoc/declarations/jsDeclarationsTypedefCommentPlacement.ts +++ b/tsc/testdata/tests/cases/conformance/jsdoc/declarations/jsDeclarationsTypedefCommentPlacement.ts @@ -157,3 +157,28 @@ export function noop() {} * @param {any} x */ export function l(x) { return x; } + +// @filename: documentationTags.js + +export function noop() {} + +/** + * Prose describing S. + * @typedef {number} S + * @see noop + * @example + * const s = 1; + */ + +export function m() {} + +// @filename: elidedImport.js + +export function noop() {} + +/** + * Nothing in the output uses this import. + * @import {Point} from "./typedef.js" + */ + +export function n() {}