mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
fix(53181): Overloads Are Not Generated as Expected When Using JsDocOverload Tag to Declare Overloads of Functions Assigned to a Prototype (#53317)
Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
This commit is contained in:
co-authored by
Nathan Shively-Sanders
parent
1d7c0c977a
commit
7e1c297730
+9
-23
@@ -305,6 +305,7 @@ import {
|
||||
getJSDocDeprecatedTag,
|
||||
getJSDocEnumTag,
|
||||
getJSDocHost,
|
||||
getJSDocOverloadTags,
|
||||
getJSDocParameterTags,
|
||||
getJSDocRoot,
|
||||
getJSDocSatisfiesExpressionType,
|
||||
@@ -15277,22 +15278,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
}
|
||||
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)) {
|
||||
const jsDocSignature = tag.typeExpression;
|
||||
if (jsDocSignature.type === undefined && !isConstructorDeclaration(decl)) {
|
||||
reportImplicitAny(jsDocSignature, anyType);
|
||||
}
|
||||
result.push(getSignatureFromDeclaration(jsDocSignature));
|
||||
hasJSDocOverloads = true;
|
||||
}
|
||||
const tags = getJSDocOverloadTags(decl);
|
||||
if (length(tags)) {
|
||||
for (const tag of tags) {
|
||||
const jsDocSignature = tag.typeExpression;
|
||||
if (jsDocSignature.type === undefined && !isConstructorDeclaration(decl)) {
|
||||
reportImplicitAny(jsDocSignature, anyType);
|
||||
}
|
||||
result.push(getSignatureFromDeclaration(jsDocSignature));
|
||||
}
|
||||
}
|
||||
if (hasJSDocOverloads) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -40514,15 +40508,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
}
|
||||
if (isInJSFile(current) && isFunctionLike(current) && current.jsDoc) {
|
||||
for (const node of current.jsDoc) {
|
||||
if (node.tags) {
|
||||
for (const tag of node.tags) {
|
||||
if (isJSDocOverloadTag(tag)) {
|
||||
hasOverloads = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
hasOverloads = length(getJSDocOverloadTags(current)) > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +158,7 @@ import {
|
||||
FunctionExpression,
|
||||
FunctionLikeDeclaration,
|
||||
GetAccessorDeclaration,
|
||||
getAllJSDocTags,
|
||||
getBaseFileName,
|
||||
GetCanonicalFileName,
|
||||
getCombinedModifierFlags,
|
||||
@@ -350,9 +351,11 @@ import {
|
||||
isWhiteSpaceLike,
|
||||
isWhiteSpaceSingleLine,
|
||||
JSDoc,
|
||||
JSDocArray,
|
||||
JSDocCallbackTag,
|
||||
JSDocEnumTag,
|
||||
JSDocMemberName,
|
||||
JSDocOverloadTag,
|
||||
JSDocParameterTag,
|
||||
JSDocPropertyLikeTag,
|
||||
JSDocSatisfiesExpression,
|
||||
@@ -4282,13 +4285,13 @@ export function getJSDocCommentsAndTags(hostNode: Node, noCache?: boolean): read
|
||||
let result: (JSDoc | JSDocTag)[] | undefined;
|
||||
// Pull parameter comments from declaring function as well
|
||||
if (isVariableLike(hostNode) && hasInitializer(hostNode) && hasJSDocNodes(hostNode.initializer!)) {
|
||||
result = addRange(result, filterOwnedJSDocTags(hostNode, last((hostNode.initializer as HasJSDoc).jsDoc!)));
|
||||
result = addRange(result, filterOwnedJSDocTags(hostNode, hostNode.initializer.jsDoc!));
|
||||
}
|
||||
|
||||
let node: Node | undefined = hostNode;
|
||||
while (node && node.parent) {
|
||||
if (hasJSDocNodes(node)) {
|
||||
result = addRange(result, filterOwnedJSDocTags(hostNode, last(node.jsDoc!)));
|
||||
result = addRange(result, filterOwnedJSDocTags(hostNode, node.jsDoc!));
|
||||
}
|
||||
|
||||
if (node.kind === SyntaxKind.Parameter) {
|
||||
@@ -4304,12 +4307,17 @@ export function getJSDocCommentsAndTags(hostNode: Node, noCache?: boolean): read
|
||||
return result || emptyArray;
|
||||
}
|
||||
|
||||
function filterOwnedJSDocTags(hostNode: Node, jsDoc: JSDoc | JSDocTag) {
|
||||
if (isJSDoc(jsDoc)) {
|
||||
const ownedTags = filter(jsDoc.tags, tag => ownsJSDocTag(hostNode, tag));
|
||||
return jsDoc.tags === ownedTags ? [jsDoc] : ownedTags;
|
||||
}
|
||||
return ownsJSDocTag(hostNode, jsDoc) ? [jsDoc] : undefined;
|
||||
function filterOwnedJSDocTags(hostNode: Node, comments: JSDocArray) {
|
||||
const lastJsDoc = last(comments);
|
||||
return flatMap<JSDoc, JSDoc | JSDocTag>(comments, jsDoc => {
|
||||
if (jsDoc === lastJsDoc) {
|
||||
const ownedTags = filter(jsDoc.tags, tag => ownsJSDocTag(hostNode, tag));
|
||||
return jsDoc.tags === ownedTags ? [jsDoc] : ownedTags;
|
||||
}
|
||||
else {
|
||||
return filter(jsDoc.tags, isJSDocOverloadTag);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4394,6 +4402,11 @@ export function getEffectiveContainerForJSDocTemplateTag(node: JSDocTemplateTag)
|
||||
return getHostSignatureFromJSDoc(node);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getJSDocOverloadTags(node: Node): readonly JSDocOverloadTag[] {
|
||||
return getAllJSDocTags(node, isJSDocOverloadTag);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getHostSignatureFromJSDoc(node: Node): SignatureDeclaration | undefined {
|
||||
const host = getEffectiveJSDocHost(node);
|
||||
|
||||
@@ -107,14 +107,69 @@ var example3 = {
|
||||
|
||||
//// [jsFileAlternativeUseOfOverloadTag.d.ts]
|
||||
declare namespace example1 {
|
||||
function constructor(value: any, options: any): void;
|
||||
/**
|
||||
* @overload Example1(value)
|
||||
* Creates Example1
|
||||
* @param value [String]
|
||||
*/
|
||||
function constructor(value: any): any;
|
||||
}
|
||||
declare namespace example2 {
|
||||
export function constructor_1(): void;
|
||||
/**
|
||||
* Example 2
|
||||
*
|
||||
* @overload Example2(value)
|
||||
* Creates Example2
|
||||
* @param value [String]
|
||||
* @param secretAccessKey [String]
|
||||
* @param sessionToken [String]
|
||||
* @example Creates with string value
|
||||
* const example = new Example('');
|
||||
* @overload Example2(options)
|
||||
* Creates Example2
|
||||
* @option options value [String]
|
||||
* @example Creates with options object
|
||||
* const example = new Example2({
|
||||
* value: '',
|
||||
* });
|
||||
*/
|
||||
export function constructor_1(value: any, secretAccessKey: any, sessionToken: any): any;
|
||||
/**
|
||||
* Example 2
|
||||
*
|
||||
* @overload Example2(value)
|
||||
* Creates Example2
|
||||
* @param value [String]
|
||||
* @param secretAccessKey [String]
|
||||
* @param sessionToken [String]
|
||||
* @example Creates with string value
|
||||
* const example = new Example('');
|
||||
* @overload Example2(options)
|
||||
* Creates Example2
|
||||
* @option options value [String]
|
||||
* @example Creates with options object
|
||||
* const example = new Example2({
|
||||
* value: '',
|
||||
* });
|
||||
*/
|
||||
export function constructor_1(): any;
|
||||
export { constructor_1 as constructor };
|
||||
}
|
||||
declare namespace example3 {
|
||||
function evaluate(options: any, callback: any): void;
|
||||
/**
|
||||
* @overload evaluate(options = {}, [callback])
|
||||
* Evaluate something
|
||||
* @note Something interesting
|
||||
* @param options [map]
|
||||
* @return [string] returns evaluation result
|
||||
* @return [null] returns nothing if callback provided
|
||||
* @callback callback function (error, result)
|
||||
* If callback is provided it will be called with evaluation result
|
||||
* @param error [Error]
|
||||
* @param result [String]
|
||||
* @see callback
|
||||
*/
|
||||
function evaluate(): any;
|
||||
}
|
||||
/**
|
||||
* function (error, result)
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
// trying to make sure that our changes do not result in any crashes here.
|
||||
|
||||
const example1 = {
|
||||
>example1 : { constructor: (value: any, options: any) => void; }
|
||||
>{ /** * @overload Example1(value) * Creates Example1 * @param value [String] */ constructor: function Example1(value, options) {},} : { constructor: (value: any, options: any) => void; }
|
||||
>example1 : { constructor: (value: any) => any; }
|
||||
>{ /** * @overload Example1(value) * Creates Example1 * @param value [String] */ constructor: function Example1(value, options) {},} : { constructor: (value: any) => any; }
|
||||
|
||||
/**
|
||||
* @overload Example1(value)
|
||||
@@ -15,17 +15,17 @@ const example1 = {
|
||||
* @param value [String]
|
||||
*/
|
||||
constructor: function Example1(value, options) {},
|
||||
>constructor : (value: any, options: any) => void
|
||||
>function Example1(value, options) {} : (value: any, options: any) => void
|
||||
>Example1 : (value: any, options: any) => void
|
||||
>constructor : (value: any) => any
|
||||
>function Example1(value, options) {} : (value: any) => any
|
||||
>Example1 : (value: any) => any
|
||||
>value : any
|
||||
>options : any
|
||||
|
||||
};
|
||||
|
||||
const example2 = {
|
||||
>example2 : { constructor: () => void; }
|
||||
>{ /** * Example 2 * * @overload Example2(value) * Creates Example2 * @param value [String] * @param secretAccessKey [String] * @param sessionToken [String] * @example Creates with string value * const example = new Example(''); * @overload Example2(options) * Creates Example2 * @option options value [String] * @example Creates with options object * const example = new Example2({ * value: '', * }); */ constructor: function Example2() {},} : { constructor: () => void; }
|
||||
>example2 : { constructor: { (value: any, secretAccessKey: any, sessionToken: any): any; (): any; }; }
|
||||
>{ /** * Example 2 * * @overload Example2(value) * Creates Example2 * @param value [String] * @param secretAccessKey [String] * @param sessionToken [String] * @example Creates with string value * const example = new Example(''); * @overload Example2(options) * Creates Example2 * @option options value [String] * @example Creates with options object * const example = new Example2({ * value: '', * }); */ constructor: function Example2() {},} : { constructor: { (value: any, secretAccessKey: any, sessionToken: any): any; (): any; }; }
|
||||
|
||||
/**
|
||||
* Example 2
|
||||
@@ -46,15 +46,15 @@ const example2 = {
|
||||
* });
|
||||
*/
|
||||
constructor: function Example2() {},
|
||||
>constructor : () => void
|
||||
>function Example2() {} : () => void
|
||||
>Example2 : () => void
|
||||
>constructor : { (value: any, secretAccessKey: any, sessionToken: any): any; (): any; }
|
||||
>function Example2() {} : { (value: any, secretAccessKey: any, sessionToken: any): any; (): any; }
|
||||
>Example2 : { (value: any, secretAccessKey: any, sessionToken: any): any; (): any; }
|
||||
|
||||
};
|
||||
|
||||
const example3 = {
|
||||
>example3 : { evaluate: (options: any, callback: any) => void; }
|
||||
>{ /** * @overload evaluate(options = {}, [callback]) * Evaluate something * @note Something interesting * @param options [map] * @return [string] returns evaluation result * @return [null] returns nothing if callback provided * @callback callback function (error, result) * If callback is provided it will be called with evaluation result * @param error [Error] * @param result [String] * @see callback */ evaluate: function evaluate(options, callback) {},} : { evaluate: (options: any, callback: any) => void; }
|
||||
>example3 : { evaluate: () => any; }
|
||||
>{ /** * @overload evaluate(options = {}, [callback]) * Evaluate something * @note Something interesting * @param options [map] * @return [string] returns evaluation result * @return [null] returns nothing if callback provided * @callback callback function (error, result) * If callback is provided it will be called with evaluation result * @param error [Error] * @param result [String] * @see callback */ evaluate: function evaluate(options, callback) {},} : { evaluate: () => any; }
|
||||
|
||||
/**
|
||||
* @overload evaluate(options = {}, [callback])
|
||||
@@ -70,9 +70,9 @@ const example3 = {
|
||||
* @see callback
|
||||
*/
|
||||
evaluate: function evaluate(options, callback) {},
|
||||
>evaluate : (options: any, callback: any) => void
|
||||
>function evaluate(options, callback) {} : (options: any, callback: any) => void
|
||||
>evaluate : (options: any, callback: any) => void
|
||||
>evaluate : () => any
|
||||
>function evaluate(options, callback) {} : () => any
|
||||
>evaluate : () => any
|
||||
>options : any
|
||||
>callback : any
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
//// [tests/cases/compiler/jsFileMethodOverloads4.ts] ////
|
||||
|
||||
//// [a.js]
|
||||
export function Foo() { }
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* @param {string} a
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* @param {number} a
|
||||
* @param {string} b
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string | number} a
|
||||
* @param {string} [b]
|
||||
* @return {void}
|
||||
*/
|
||||
Foo.prototype.bar = function (a, b) { }
|
||||
|
||||
|
||||
|
||||
|
||||
//// [a.d.ts]
|
||||
export function Foo(): void;
|
||||
export class Foo {
|
||||
/**
|
||||
* @overload
|
||||
* @param {string} a
|
||||
* @return {void}
|
||||
*/
|
||||
bar(a: string): void;
|
||||
/**
|
||||
* @overload
|
||||
* @param {number} a
|
||||
* @param {string} b
|
||||
* @return {void}
|
||||
*/
|
||||
bar(a: number, b: string): void;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//// [tests/cases/compiler/jsFileMethodOverloads4.ts] ////
|
||||
|
||||
=== /a.js ===
|
||||
export function Foo() { }
|
||||
>Foo : Symbol(Foo, Decl(a.js, 0, 0))
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* @param {string} a
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* @param {number} a
|
||||
* @param {string} b
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string | number} a
|
||||
* @param {string} [b]
|
||||
* @return {void}
|
||||
*/
|
||||
Foo.prototype.bar = function (a, b) { }
|
||||
>Foo.prototype : Symbol(Foo.bar, Decl(a.js, 0, 25))
|
||||
>Foo : Symbol(Foo, Decl(a.js, 0, 0))
|
||||
>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --))
|
||||
>bar : Symbol(Foo.bar, Decl(a.js, 0, 25))
|
||||
>a : Symbol(a, Decl(a.js, 20, 30))
|
||||
>b : Symbol(b, Decl(a.js, 20, 32))
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
//// [tests/cases/compiler/jsFileMethodOverloads4.ts] ////
|
||||
|
||||
=== /a.js ===
|
||||
export function Foo() { }
|
||||
>Foo : typeof Foo
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* @param {string} a
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* @param {number} a
|
||||
* @param {string} b
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string | number} a
|
||||
* @param {string} [b]
|
||||
* @return {void}
|
||||
*/
|
||||
Foo.prototype.bar = function (a, b) { }
|
||||
>Foo.prototype.bar = function (a, b) { } : { (a: string): void; (a: number, b: string): void; }
|
||||
>Foo.prototype.bar : any
|
||||
>Foo.prototype : any
|
||||
>Foo : typeof Foo
|
||||
>prototype : any
|
||||
>bar : any
|
||||
>function (a, b) { } : { (a: string): void; (a: number, b: string): void; }
|
||||
>a : string | number
|
||||
>b : string
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
//// [tests/cases/compiler/jsFileMethodOverloads5.ts] ////
|
||||
|
||||
//// [a.js]
|
||||
/**
|
||||
* @overload
|
||||
* @param {string} a
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* @param {number} a
|
||||
* @param {number} [b]
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string | number} a
|
||||
* @param {number} [b]
|
||||
*/
|
||||
export const foo = function (a, b) { }
|
||||
|
||||
|
||||
|
||||
|
||||
//// [a.d.ts]
|
||||
/**
|
||||
* @overload
|
||||
* @param {string} a
|
||||
* @return {void}
|
||||
*/
|
||||
export function foo(a: string): void;
|
||||
/**
|
||||
* @overload
|
||||
* @param {number} a
|
||||
* @param {number} [b]
|
||||
* @return {void}
|
||||
*/
|
||||
export function foo(a: number, b?: number): void;
|
||||
@@ -0,0 +1,25 @@
|
||||
//// [tests/cases/compiler/jsFileMethodOverloads5.ts] ////
|
||||
|
||||
=== /a.js ===
|
||||
/**
|
||||
* @overload
|
||||
* @param {string} a
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* @param {number} a
|
||||
* @param {number} [b]
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string | number} a
|
||||
* @param {number} [b]
|
||||
*/
|
||||
export const foo = function (a, b) { }
|
||||
>foo : Symbol(foo, Decl(a.js, 17, 12))
|
||||
>a : Symbol(a, Decl(a.js, 17, 29))
|
||||
>b : Symbol(b, Decl(a.js, 17, 31))
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
//// [tests/cases/compiler/jsFileMethodOverloads5.ts] ////
|
||||
|
||||
=== /a.js ===
|
||||
/**
|
||||
* @overload
|
||||
* @param {string} a
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* @param {number} a
|
||||
* @param {number} [b]
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string | number} a
|
||||
* @param {number} [b]
|
||||
*/
|
||||
export const foo = function (a, b) { }
|
||||
>foo : { (a: string): void; (a: number, b?: number): void; }
|
||||
>function (a, b) { } : { (a: string): void; (a: number, b?: number): void; }
|
||||
>a : string | number
|
||||
>b : number
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// @declaration: true
|
||||
// @emitDeclarationOnly: true
|
||||
// @allowJs: true
|
||||
// @filename: /a.js
|
||||
export function Foo() { }
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* @param {string} a
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* @param {number} a
|
||||
* @param {string} b
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string | number} a
|
||||
* @param {string} [b]
|
||||
* @return {void}
|
||||
*/
|
||||
Foo.prototype.bar = function (a, b) { }
|
||||
@@ -0,0 +1,23 @@
|
||||
// @declaration: true
|
||||
// @emitDeclarationOnly: true
|
||||
// @allowJs: true
|
||||
// @filename: /a.js
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* @param {string} a
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* @param {number} a
|
||||
* @param {number} [b]
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string | number} a
|
||||
* @param {number} [b]
|
||||
*/
|
||||
export const foo = function (a, b) { }
|
||||
Reference in New Issue
Block a user