Skip to content

Add support for "specialize" tag#59666

Open
apendua wants to merge 2 commits intomicrosoft:mainfrom
apendua:fix27387
Open

Add support for "specialize" tag#59666
apendua wants to merge 2 commits intomicrosoft:mainfrom
apendua:fix27387

Conversation

@apendua
Copy link
Contributor

@apendua apendua commented Aug 17, 2024

Fixes #27387

This is an implementation proposal for @specialize JSDoc tag, which will allow specifying type arguments in JS files. For example if f has a type parameter T, the following JSDoc syntax:

let x = /** @specialize {number} */(f());

will be equivalent to TS:

let x = f<number>();

The tag can be also attached to any other parent node, e.g. assignment statement would also do:

/** @specialize {number} */
let x = f();

Currently, it is also possible to wrap type arguments with <...> as in /** @specialize <number> */,
if that's the preference. I couldn't make up my mind so I allowed both options for now.

The following expressions are also supported:

  • tagged template expression
  • "new" expression
  • JSX element and self-closing tag

I am still planning to add more unit tests, e.g.

  • check if "find all references" works
  • test JSX syntax

Having said that, I've decided to open this PR early (as draft), because there are some open questions where I seek your advice:

  • Is @specialize a good name for this tag? I was also considering @typeArgs, which I think would be a little bit cleaner in communicating its purpose, but I don't like the fact that it consists of two words, as so far there has been no precedence.
  • One issue with @specialize is the fact that some people would prefer to spell it "specialise" and I am worried that it may cause confusion and typing errors in the future.

@typescript-bot typescript-bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Aug 17, 2024
@typescript-bot
Copy link
Collaborator

Looks like you're introducing a change to the public API surface area. If this includes breaking changes, please document them on our wiki's API Breaking Changes page.

Also, please make sure @DanielRosenwasser and @RyanCavanaugh are aware of the changes, just as a heads up.

@apendua apendua force-pushed the fix27387 branch 4 times, most recently from d0da5cb to b819a75 Compare August 21, 2024 13:26
@apendua apendua marked this pull request as ready for review August 30, 2024 07:21
@typescript-bot
Copy link
Collaborator

The TypeScript team hasn't accepted the linked issue #27387. If you can get it accepted, this PR will have a better chance of being reviewed.

@Gerrit0
Copy link
Contributor

Gerrit0 commented Sep 22, 2024

Currently, it is also possible to wrap type arguments with <...> as in /** @specialize <number> */,
if that's the preference. I couldn't make up my mind so I allowed both options for now.

Please no. That isn't consistent with how any other type comments are parsed, the extra edge case doesn't just affect TS. @specialize {number} is consistent with @type

Copilot AI review requested due to automatic review settings February 17, 2026 22:49
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new JSDoc tag, @specialize, to allow explicitly supplying type arguments in JavaScript / JSDoc-typed code (including JSX), similar to f<number>() in TypeScript. It updates the compiler/parser/AST/public API surface to represent the tag, teaches the checker to consume it for call-like expressions, and adds conformance + fourslash coverage and baselines.

Changes:

  • Add a new AST node kind (JSDocSpecializeTag) with parser/binder/emitter/factory support and public API typings.
  • Extend type-checking to read @specialize-provided type arguments for call/new/tagged-template/JSX call-like expressions in JS files.
  • Add conformance and fourslash tests + baselines, and include specialize in JSDoc tag name completions.

Reviewed changes

Copilot reviewed 45 out of 46 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/compiler/types.ts Adds SyntaxKind.JSDocSpecializeTag and JSDocSpecializeTag AST interface + factory API typings.
src/compiler/parser.ts Parses @specialize and wires forEachChild traversal for the new node.
src/compiler/binder.ts Binds @specialize tag typeArguments.
src/compiler/factory/nodeTests.ts Adds isJSDocSpecializeTag type guard.
src/compiler/factory/nodeFactory.ts Adds create/updateJSDocSpecializeTag and default tag name mapping.
src/compiler/emitter.ts Adds printer support for emitting @specialize tags.
src/compiler/utilities.ts Updates JSDoc tag ownership logic so @specialize behaves like @type/@satisfies on parenthesized expressions.
src/compiler/utilitiesPublic.ts Exposes getJSDocSpecializeTag in the public utilities surface.
src/compiler/checker.ts Consumes @specialize type arguments for call-like expressions in JS (including JSX).
src/services/jsDoc.ts Adds specialize to tag-name completions and QuickInfo tag display formatting.
src/services/classifier.ts Adds syntactic classification support for @specialize type arguments in doc comments.
src/services/codefixes/fixAddVoidToPromise.ts Recognizes @specialize as an alternative to @type {Promise<...>} for the Promise codefix flow in JS.
tests/cases/conformance/jsdoc/specializeTag1.ts Conformance coverage: @specialize on function call result / parenthesized expression.
tests/cases/conformance/jsdoc/specializeTag2.ts Conformance coverage: @specialize on tagged template expression, incl. error cases.
tests/cases/conformance/jsdoc/specializeTag3.ts Conformance coverage: @specialize on new expressions.
tests/cases/conformance/jsdoc/specializeTag4.ts Conformance coverage: constraint checking for specialized type arguments.
tests/cases/conformance/jsdoc/specializeTag5.ts Conformance coverage: specializing DOM generic return types (e.g. querySelector).
tests/cases/conformance/jsdoc/specializeTag6.ts Conformance coverage: specializing JSX elements (React JSX) + error cases.
tests/cases/fourslash/quickInfoJSDocSpecializeTagInJsx.ts Fourslash coverage: QuickInfo for JSX specialization/inference.
tests/cases/fourslash/referencesForSpecializeTag.ts Fourslash coverage: find-all-references through @specialize type args.
tests/baselines/reference/specializeTag1.* Baselines for specializeTag1 conformance case.
tests/baselines/reference/specializeTag2.* Baselines for specializeTag2 conformance case.
tests/baselines/reference/specializeTag3.* Baselines for specializeTag3 conformance case.
tests/baselines/reference/specializeTag4.* Baselines for specializeTag4 conformance case.
tests/baselines/reference/specializeTag5.* Baselines for specializeTag5 conformance case.
tests/baselines/reference/specializeTag6.* Baselines for specializeTag6 conformance case.
tests/baselines/reference/referencesForSpecializeTag.baseline.jsonc Baseline output for the new find-all-references fourslash test.
tests/baselines/reference/jsdocParameterTagSnippetCompletion1.baseline Updates JSDoc tag snippet completion baselines to include specialize.
tests/baselines/reference/jsdocParameterTagSnippetCompletion2.baseline Updates JSDoc tag snippet completion baselines to include @specialize.
tests/baselines/reference/jsdocParameterTagSnippetCompletion3.baseline Updates JSDoc tag snippet completion baselines to include specialize.
tests/baselines/reference/api/typescript.d.ts Updates public d.ts baseline for new SyntaxKind + JSDocSpecializeTag APIs.

displayParts.push(...[punctuationPart(SyntaxKind.CommaToken), spacePart()]);
}
});
}
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getCommentDisplayParts drops the tag’s trailing comment for JSDocSpecializeTag (unlike @template, which appends comment display parts). This means any text after @specialize won’t show up in Quick Info / JSDoc tag info. Consider appending spacePart() + getDisplayPartsFromComment(comment, checker) when comment is present.

Suggested change
}
}
if (comment) {
displayParts.push(...[spacePart(), ...getDisplayPartsFromComment(comment, checker)]);
}

Copilot uses AI. Check for mistakes.
Comment on lines +852 to +854
const typeArguments = (tag as JSDocSpecializeTag).typeArguments;
typeArguments.forEach(processElement);
commentStart = typeArguments.end;
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In classifyJSDocComment, the JSDocSpecializeTag branch calls processElement on each typeArgument, but it never advances pos past the type argument list. As a result, the later pushCommentRange(pos, ...) (either at the next tag or at the end of the doc comment) will still classify the whole remainder of the tag as comment, overlapping the classifications produced by processElement and potentially swallowing </>/{/}/commas. Consider mirroring the @param handling: push a comment range up to each type arg, classify it, update pos to typeArg.end, then set pos/commentStart to the end of the closing delimiter before classifying the trailing comment text.

Suggested change
const typeArguments = (tag as JSDocSpecializeTag).typeArguments;
typeArguments.forEach(processElement);
commentStart = typeArguments.end;
const specializeTag = tag as JSDocSpecializeTag;
const typeArguments = specializeTag.typeArguments;
for (const typeArg of typeArguments) {
if (pos < typeArg.pos) {
pushCommentRange(pos, typeArg.pos - pos);
}
processElement(typeArg);
pos = typeArg.end;
}
pos = commentStart = typeArguments.end;

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow to explicitly pass type parameters via JSDoc

3 participants