mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
JSDoc overload tag (#51234)
* Add support for JSDocOverloadTag * Use overload tag to determine function type * Update baselines * Add new tests along with baselines * Add tests for all @overload tags in one comment * Add tests for find-all-ref and rename operations * Add tests for alternative uses of @overload tag Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
This commit is contained in:
co-authored by
Nathan Shively-Sanders
parent
4076ff8fd6
commit
e4816ed44c
@@ -220,6 +220,7 @@ import {
|
||||
JSDocClassTag,
|
||||
JSDocEnumTag,
|
||||
JSDocFunctionType,
|
||||
JSDocOverloadTag,
|
||||
JSDocParameterTag,
|
||||
JSDocPropertyLikeTag,
|
||||
JSDocSignature,
|
||||
@@ -2966,6 +2967,8 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
|
||||
case SyntaxKind.JSDocCallbackTag:
|
||||
case SyntaxKind.JSDocEnumTag:
|
||||
return (delayedTypeAliases || (delayedTypeAliases = [])).push(node as JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag);
|
||||
case SyntaxKind.JSDocOverloadTag:
|
||||
return bind((node as JSDocOverloadTag).typeExpression);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -549,6 +549,7 @@ import {
|
||||
isJSDocNullableType,
|
||||
isJSDocOptionalParameter,
|
||||
isJSDocOptionalType,
|
||||
isJSDocOverloadTag,
|
||||
isJSDocParameterTag,
|
||||
isJSDocPropertyLikeTag,
|
||||
isJSDocPropertyTag,
|
||||
@@ -14270,6 +14271,22 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (isInJSFile(decl) && decl.jsDoc) {
|
||||
let hasJSDocOverloads = false;
|
||||
for (const node of decl.jsDoc) {
|
||||
if (node.tags) {
|
||||
for (const tag of node.tags) {
|
||||
if (isJSDocOverloadTag(tag)) {
|
||||
result.push(getSignatureFromDeclaration(tag.typeExpression));
|
||||
hasJSDocOverloads = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasJSDocOverloads) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// If this is a function or method declaration, get the signature from the @type tag for the sake of optional parameters.
|
||||
// Exclude contextually-typed kinds because we already apply the @type tag to the context, plus applying it here to the initializer would supress checks that the two are compatible.
|
||||
result.push(
|
||||
|
||||
@@ -261,6 +261,7 @@ import {
|
||||
JSDocNonNullableType,
|
||||
JSDocNullableType,
|
||||
JSDocOptionalType,
|
||||
JSDocOverloadTag,
|
||||
JSDocPropertyLikeTag,
|
||||
JSDocReturnTag,
|
||||
JSDocSeeTag,
|
||||
@@ -2117,6 +2118,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
|
||||
return;
|
||||
case SyntaxKind.JSDocCallbackTag:
|
||||
return emitJSDocCallbackTag(node as JSDocCallbackTag);
|
||||
case SyntaxKind.JSDocOverloadTag:
|
||||
return emitJSDocOverloadTag(node as JSDocOverloadTag);
|
||||
// SyntaxKind.JSDocEnumTag (see below)
|
||||
case SyntaxKind.JSDocParameterTag:
|
||||
case SyntaxKind.JSDocPropertyTag:
|
||||
@@ -4375,6 +4378,11 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
|
||||
emitJSDocSignature(tag.typeExpression);
|
||||
}
|
||||
|
||||
function emitJSDocOverloadTag(tag: JSDocOverloadTag) {
|
||||
emitJSDocComment(tag.comment);
|
||||
emitJSDocSignature(tag.typeExpression);
|
||||
}
|
||||
|
||||
function emitJSDocSimpleTag(tag: JSDocTag) {
|
||||
emitJSDocTagName(tag.tagName);
|
||||
emitJSDocComment(tag.comment);
|
||||
|
||||
@@ -239,6 +239,7 @@ import {
|
||||
JSDocNonNullableType,
|
||||
JSDocNullableType,
|
||||
JSDocOptionalType,
|
||||
JSDocOverloadTag,
|
||||
JSDocOverrideTag,
|
||||
JSDocParameterTag,
|
||||
JSDocPrivateTag,
|
||||
@@ -830,6 +831,8 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
|
||||
updateJSDocPropertyTag,
|
||||
createJSDocCallbackTag,
|
||||
updateJSDocCallbackTag,
|
||||
createJSDocOverloadTag,
|
||||
updateJSDocOverloadTag,
|
||||
createJSDocAugmentsTag,
|
||||
updateJSDocAugmentsTag,
|
||||
createJSDocImplementsTag,
|
||||
@@ -5305,6 +5308,22 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
|
||||
: node;
|
||||
}
|
||||
|
||||
// @api
|
||||
function createJSDocOverloadTag(tagName: Identifier | undefined, typeExpression: JSDocSignature, comment?: string | NodeArray<JSDocComment>): JSDocOverloadTag {
|
||||
const node = createBaseJSDocTag<JSDocOverloadTag>(SyntaxKind.JSDocOverloadTag, tagName ?? createIdentifier("overload"), comment);
|
||||
node.typeExpression = typeExpression;
|
||||
return node;
|
||||
}
|
||||
|
||||
// @api
|
||||
function updateJSDocOverloadTag(node: JSDocOverloadTag, tagName: Identifier = getDefaultTagName(node), typeExpression: JSDocSignature, comment: string | NodeArray<JSDocComment> | undefined): JSDocOverloadTag {
|
||||
return node.tagName !== tagName
|
||||
|| node.typeExpression !== typeExpression
|
||||
|| node.comment !== comment
|
||||
? update(createJSDocOverloadTag(tagName, typeExpression, comment), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
// @api
|
||||
function createJSDocAugmentsTag(tagName: Identifier | undefined, className: JSDocAugmentsTag["class"], comment?: string | NodeArray<JSDocComment>): JSDocAugmentsTag {
|
||||
const node = createBaseJSDocTag<JSDocAugmentsTag>(SyntaxKind.JSDocAugmentsTag, tagName ?? createIdentifier("augments"), comment);
|
||||
@@ -7193,6 +7212,7 @@ function getDefaultTagNameForKind(kind: JSDocTag["kind"]): string {
|
||||
case SyntaxKind.JSDocParameterTag: return "param";
|
||||
case SyntaxKind.JSDocPropertyTag: return "prop";
|
||||
case SyntaxKind.JSDocCallbackTag: return "callback";
|
||||
case SyntaxKind.JSDocOverloadTag: return "overload";
|
||||
case SyntaxKind.JSDocAugmentsTag: return "augments";
|
||||
case SyntaxKind.JSDocImplementsTag: return "implements";
|
||||
default:
|
||||
|
||||
@@ -98,6 +98,7 @@ import {
|
||||
JSDocNonNullableType,
|
||||
JSDocNullableType,
|
||||
JSDocOptionalType,
|
||||
JSDocOverloadTag,
|
||||
JSDocOverrideTag,
|
||||
JSDocParameterTag,
|
||||
JSDocPrivateTag,
|
||||
@@ -1135,6 +1136,10 @@ export function isJSDocOverrideTag(node: Node): node is JSDocOverrideTag {
|
||||
return node.kind === SyntaxKind.JSDocOverrideTag;
|
||||
}
|
||||
|
||||
export function isJSDocOverloadTag(node: Node): node is JSDocOverloadTag {
|
||||
return node.kind === SyntaxKind.JSDocOverloadTag;
|
||||
}
|
||||
|
||||
export function isJSDocDeprecatedTag(node: Node): node is JSDocDeprecatedTag {
|
||||
return node.kind === SyntaxKind.JSDocDeprecatedTag;
|
||||
}
|
||||
|
||||
+24
-5
@@ -177,6 +177,7 @@ import {
|
||||
JSDocNonNullableType,
|
||||
JSDocNullableType,
|
||||
JSDocOptionalType,
|
||||
JSDocOverloadTag,
|
||||
JSDocOverrideTag,
|
||||
JSDocParameterTag,
|
||||
JSDocPrivateTag,
|
||||
@@ -8782,6 +8783,9 @@ namespace Parser {
|
||||
case "callback":
|
||||
tag = parseCallbackTag(start, tagName, margin, indentText);
|
||||
break;
|
||||
case "overload":
|
||||
tag = parseOverloadTag(start, tagName, margin, indentText);
|
||||
break;
|
||||
case "see":
|
||||
tag = parseSeeTag(start, tagName, margin, indentText);
|
||||
break;
|
||||
@@ -9275,10 +9279,7 @@ namespace Parser {
|
||||
return createNodeArray(parameters || [], pos);
|
||||
}
|
||||
|
||||
function parseCallbackTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocCallbackTag {
|
||||
const fullName = parseJSDocTypeNameWithNamespace();
|
||||
skipWhitespace();
|
||||
let comment = parseTagComments(indent);
|
||||
function parseJSDocSignature(start: number, indent: number): JSDocSignature {
|
||||
const parameters = parseCallbackTagParameters(indent);
|
||||
const returnTag = tryParse(() => {
|
||||
if (parseOptionalJsdoc(SyntaxKind.AtToken)) {
|
||||
@@ -9288,7 +9289,14 @@ namespace Parser {
|
||||
}
|
||||
}
|
||||
});
|
||||
const typeExpression = finishNode(factory.createJSDocSignature(/*typeParameters*/ undefined, parameters, returnTag), start);
|
||||
return finishNode(factory.createJSDocSignature(/*typeParameters*/ undefined, parameters, returnTag), start);
|
||||
}
|
||||
|
||||
function parseCallbackTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocCallbackTag {
|
||||
const fullName = parseJSDocTypeNameWithNamespace();
|
||||
skipWhitespace();
|
||||
let comment = parseTagComments(indent);
|
||||
const typeExpression = parseJSDocSignature(start, indent);
|
||||
if (!comment) {
|
||||
comment = parseTrailingTagComments(start, getNodePos(), indent, indentText);
|
||||
}
|
||||
@@ -9296,6 +9304,17 @@ namespace Parser {
|
||||
return finishNode(factory.createJSDocCallbackTag(tagName, typeExpression, fullName, comment), start, end);
|
||||
}
|
||||
|
||||
function parseOverloadTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocOverloadTag {
|
||||
skipWhitespace();
|
||||
let comment = parseTagComments(indent);
|
||||
const typeExpression = parseJSDocSignature(start, indent);
|
||||
if (!comment) {
|
||||
comment = parseTrailingTagComments(start, getNodePos(), indent, indentText);
|
||||
}
|
||||
const end = comment !== undefined ? getNodePos() : typeExpression.end;
|
||||
return finishNode(factory.createJSDocOverloadTag(tagName, typeExpression, comment), start, end);
|
||||
}
|
||||
|
||||
function escapedTextsEqual(a: EntityName, b: EntityName): boolean {
|
||||
while (!ts.isIdentifier(a) || !ts.isIdentifier(b)) {
|
||||
if (!ts.isIdentifier(a) && !ts.isIdentifier(b) && a.right.escapedText === b.right.escapedText) {
|
||||
|
||||
@@ -423,6 +423,7 @@ export const enum SyntaxKind {
|
||||
JSDocReadonlyTag,
|
||||
JSDocOverrideTag,
|
||||
JSDocCallbackTag,
|
||||
JSDocOverloadTag,
|
||||
JSDocEnumTag,
|
||||
JSDocParameterTag,
|
||||
JSDocReturnTag,
|
||||
@@ -4065,6 +4066,13 @@ export interface JSDocCallbackTag extends JSDocTag, NamedDeclaration, LocalsCont
|
||||
readonly typeExpression: JSDocSignature;
|
||||
}
|
||||
|
||||
|
||||
export interface JSDocOverloadTag extends JSDocTag {
|
||||
readonly kind: SyntaxKind.JSDocOverloadTag;
|
||||
readonly parent: JSDoc;
|
||||
readonly typeExpression: JSDocSignature;
|
||||
}
|
||||
|
||||
export interface JSDocThrowsTag extends JSDocTag {
|
||||
readonly kind: SyntaxKind.JSDocThrowsTag;
|
||||
readonly typeExpression?: JSDocTypeExpression;
|
||||
@@ -8524,6 +8532,8 @@ export interface NodeFactory {
|
||||
updateJSDocEnumTag(node: JSDocEnumTag, tagName: Identifier | undefined, typeExpression: JSDocTypeExpression, comment: string | NodeArray<JSDocComment> | undefined): JSDocEnumTag;
|
||||
createJSDocCallbackTag(tagName: Identifier | undefined, typeExpression: JSDocSignature, fullName?: Identifier | JSDocNamespaceDeclaration, comment?: string | NodeArray<JSDocComment>): JSDocCallbackTag;
|
||||
updateJSDocCallbackTag(node: JSDocCallbackTag, tagName: Identifier | undefined, typeExpression: JSDocSignature, fullName: Identifier | JSDocNamespaceDeclaration | undefined, comment: string | NodeArray<JSDocComment> | undefined): JSDocCallbackTag;
|
||||
createJSDocOverloadTag(tagName: Identifier | undefined, typeExpression: JSDocSignature, comment?: string | NodeArray<JSDocComment>): JSDocOverloadTag;
|
||||
updateJSDocOverloadTag(node: JSDocOverloadTag, tagName: Identifier | undefined, typeExpression: JSDocSignature, comment: string | NodeArray<JSDocComment> | undefined): JSDocOverloadTag;
|
||||
createJSDocAugmentsTag(tagName: Identifier | undefined, className: JSDocAugmentsTag["class"], comment?: string | NodeArray<JSDocComment>): JSDocAugmentsTag;
|
||||
updateJSDocAugmentsTag(node: JSDocAugmentsTag, tagName: Identifier | undefined, className: JSDocAugmentsTag["class"], comment: string | NodeArray<JSDocComment> | undefined): JSDocAugmentsTag;
|
||||
createJSDocImplementsTag(tagName: Identifier | undefined, className: JSDocImplementsTag["class"], comment?: string | NodeArray<JSDocComment>): JSDocImplementsTag;
|
||||
|
||||
@@ -271,6 +271,7 @@ import {
|
||||
isJSDocMemberName,
|
||||
isJSDocNameReference,
|
||||
isJSDocNode,
|
||||
isJSDocOverloadTag,
|
||||
isJSDocParameterTag,
|
||||
isJSDocPropertyLikeTag,
|
||||
isJSDocSignature,
|
||||
@@ -5801,7 +5802,7 @@ export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParam
|
||||
|
||||
/** template tags are only available when a typedef isn't already using them */
|
||||
function isNonTypeAliasTemplate(tag: JSDocTag): tag is JSDocTemplateTag {
|
||||
return isJSDocTemplateTag(tag) && !(tag.parent.kind === SyntaxKind.JSDoc && tag.parent.tags!.some(isJSDocTypeAlias));
|
||||
return isJSDocTemplateTag(tag) && !(tag.parent.kind === SyntaxKind.JSDoc && (tag.parent.tags!.some(isJSDocTypeAlias) || tag.parent.tags!.some(isJSDocOverloadTag)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -124,6 +124,7 @@ import {
|
||||
isJSDocEnumTag,
|
||||
isJSDocFunctionType,
|
||||
isJSDocImplementsTag,
|
||||
isJSDocOverloadTag,
|
||||
isJSDocOverrideTag,
|
||||
isJSDocParameterTag,
|
||||
isJSDocPrivateTag,
|
||||
@@ -1210,6 +1211,14 @@ function formatJSDocLink(link: JSDocLink | JSDocLinkCode | JSDocLinkPlain) {
|
||||
*/
|
||||
export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): readonly TypeParameterDeclaration[] {
|
||||
if (isJSDocSignature(node)) {
|
||||
if (isJSDoc(node.parent)) {
|
||||
const overloadTag = find(node.parent.tags, (tag) => {
|
||||
return isJSDocOverloadTag(tag) && tag.typeExpression === node;
|
||||
});
|
||||
if (overloadTag) {
|
||||
return flatMap(node.parent.tags, tag => isJSDocTemplateTag(tag) ? tag.typeParameters : undefined);
|
||||
}
|
||||
}
|
||||
return emptyArray;
|
||||
}
|
||||
if (isJSDocTypeAlias(node)) {
|
||||
|
||||
Reference in New Issue
Block a user