fix(41526): add JSDoc type annotations before parameters (#41561)
This commit is contained in:
parent
9ae375fcd5
commit
2946318df0
|
@ -337,17 +337,46 @@ namespace ts.codefix {
|
|||
if (!signature) {
|
||||
return;
|
||||
}
|
||||
const paramTags = mapDefined(parameterInferences, inference => {
|
||||
|
||||
const inferences = mapDefined(parameterInferences, inference => {
|
||||
const param = inference.declaration;
|
||||
// only infer parameters that have (1) no type and (2) an accessible inferred type
|
||||
if (param.initializer || getJSDocType(param) || !isIdentifier(param.name)) return;
|
||||
|
||||
if (param.initializer || getJSDocType(param) || !isIdentifier(param.name)) {
|
||||
return;
|
||||
}
|
||||
const typeNode = inference.type && getTypeNodeIfAccessible(inference.type, param, program, host);
|
||||
const name = factory.cloneNode(param.name);
|
||||
setEmitFlags(name, EmitFlags.NoComments | EmitFlags.NoNestedComments);
|
||||
return typeNode && factory.createJSDocParameterTag(/*tagName*/ undefined, name, /*isBracketed*/ !!inference.isOptional, factory.createJSDocTypeExpression(typeNode), /* isNameFirst */ false, "");
|
||||
if (typeNode) {
|
||||
const name = factory.cloneNode(param.name);
|
||||
setEmitFlags(name, EmitFlags.NoComments | EmitFlags.NoNestedComments);
|
||||
return { name: factory.cloneNode(param.name), param, isOptional: !!inference.isOptional, typeNode };
|
||||
}
|
||||
});
|
||||
addJSDocTags(changes, sourceFile, signature, paramTags);
|
||||
|
||||
if (!inferences.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isArrowFunction(signature) || isFunctionExpression(signature)) {
|
||||
const needParens = isArrowFunction(signature) && !findChildOfKind(signature, SyntaxKind.OpenParenToken, sourceFile);
|
||||
if (needParens) {
|
||||
changes.insertNodeBefore(sourceFile, first(signature.parameters), factory.createToken(SyntaxKind.OpenParenToken));
|
||||
}
|
||||
|
||||
forEach(inferences, ({ typeNode, param }) => {
|
||||
const typeTag = factory.createJSDocTypeTag(/*tagName*/ undefined, factory.createJSDocTypeExpression(typeNode));
|
||||
const jsDoc = factory.createJSDocComment(/*comment*/ undefined, [typeTag]);
|
||||
changes.insertNodeAt(sourceFile, param.getStart(sourceFile), jsDoc, { suffix: " " });
|
||||
});
|
||||
|
||||
if (needParens) {
|
||||
changes.insertNodeAfter(sourceFile, last(signature.parameters), factory.createToken(SyntaxKind.CloseParenToken));
|
||||
}
|
||||
}
|
||||
else {
|
||||
const paramTags = map(inferences, ({ name, typeNode, isOptional }) =>
|
||||
factory.createJSDocParameterTag(/*tagName*/ undefined, name, /*isBracketed*/ !!isOptional, factory.createJSDocTypeExpression(typeNode), /* isNameFirst */ false, ""));
|
||||
addJSDocTags(changes, sourceFile, signature, paramTags);
|
||||
}
|
||||
}
|
||||
|
||||
export function addJSDocTags(changes: textChanges.ChangeTracker, sourceFile: SourceFile, parent: HasJSDoc, newTags: readonly JSDocTag[]): void {
|
||||
|
|
|
@ -14,14 +14,8 @@ verify.codeFixAll({
|
|||
fixId: "inferFromUsage",
|
||||
fixAllDescription: "Infer all types from usage",
|
||||
newFileContent:
|
||||
`/**
|
||||
* @param {{ y: number; }} x
|
||||
*/
|
||||
const foo = x => x.y + 1;
|
||||
`const foo = (/** @type {{ y: number; }} */ x) => x.y + 1;
|
||||
class C {
|
||||
/**
|
||||
* @param {{ y: number; }} x
|
||||
*/
|
||||
m = x => x.y + 1;
|
||||
m = (/** @type {{ y: number; }} */ x) => x.y + 1;
|
||||
}`,
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @noImplicitAny: false
|
||||
|
||||
// @filename: /foo.js
|
||||
////function foo(names) {
|
||||
//// return names.filter((name, index) => name === "foo" && index === 1);
|
||||
////}
|
||||
|
||||
verify.codeFix({
|
||||
description: "Infer parameter types from usage",
|
||||
index: 1,
|
||||
newFileContent:
|
||||
`function foo(names) {
|
||||
return names.filter((/** @type {string} */ name, /** @type {number} */ index) => name === "foo" && index === 1);
|
||||
}`
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @noImplicitAny: false
|
||||
|
||||
// @filename: /foo.js
|
||||
////function foo(names) {
|
||||
//// return names.filter(name => name === "foo");
|
||||
////}
|
||||
|
||||
verify.codeFix({
|
||||
description: "Infer parameter types from usage",
|
||||
index: 1,
|
||||
newFileContent:
|
||||
`function foo(names) {
|
||||
return names.filter((/** @type {string} */ name) => name === "foo");
|
||||
}`
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @noImplicitAny: false
|
||||
|
||||
// @filename: /foo.js
|
||||
////function foo(names) {
|
||||
//// return names.filter(function (name) {
|
||||
//// return name === "foo";
|
||||
//// });
|
||||
////}
|
||||
|
||||
verify.codeFix({
|
||||
description: "Infer parameter types from usage",
|
||||
index: 1,
|
||||
newFileContent:
|
||||
`function foo(names) {
|
||||
return names.filter(function (/** @type {string} */ name) {
|
||||
return name === "foo";
|
||||
});
|
||||
}`
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @noImplicitAny: false
|
||||
|
||||
// @filename: /foo.js
|
||||
////function foo(names) {
|
||||
//// return names.filter(function (name, index) {
|
||||
//// return name === "foo" && index === 1;
|
||||
//// });
|
||||
////}
|
||||
|
||||
verify.codeFix({
|
||||
description: "Infer parameter types from usage",
|
||||
index: 1,
|
||||
newFileContent:
|
||||
`function foo(names) {
|
||||
return names.filter(function (/** @type {string} */ name, /** @type {number} */ index) {
|
||||
return name === "foo" && index === 1;
|
||||
});
|
||||
}`
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @noImplicitAny: false
|
||||
|
||||
// @filename: /foo.js
|
||||
////const foo = [x => x + 1];
|
||||
|
||||
verify.codeFix({
|
||||
description: "Infer parameter types from usage",
|
||||
index: 0,
|
||||
newFileContent: `const foo = [(/** @type {number} */ x) => x + 1];`
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @noImplicitAny: false
|
||||
|
||||
// @filename: /foo.js
|
||||
////const foo = [(/** @type {number} */ x) => x + 1];
|
||||
|
||||
verify.not.codeFixAvailable();
|
|
@ -0,0 +1,11 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @noImplicitAny: false
|
||||
|
||||
// @filename: /foo.js
|
||||
/////** @type {(x: number) => number} */
|
||||
////const foo = x => x + 1;
|
||||
|
||||
verify.not.codeFixAvailable();
|
Loading…
Reference in a new issue