fix(41526): add JSDoc type annotations before parameters (#41561)

This commit is contained in:
Oleksandr T 2020-12-05 02:52:12 +02:00 committed by GitHub
parent 9ae375fcd5
commit 2946318df0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 157 additions and 15 deletions

View file

@ -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 {

View file

@ -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;
}`,
});

View file

@ -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);
}`
});

View file

@ -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");
}`
});

View file

@ -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";
});
}`
});

View file

@ -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;
});
}`
});

View file

@ -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];`
});

View file

@ -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();

View file

@ -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();