From 594430f113f16a7d144ae8cc9fdc3789add01b5a Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 4 Dec 2018 09:03:08 -0800 Subject: [PATCH] Infer from arrows from usage. (#28832) * Infer from arrows from usage. Previously only function expressions were, and only those with an easily accessible name. Now any arrow function or function expression will infer from usage. * remove isApplicableFunctionForInference *all* functions are applicable for inference now. --- src/services/codefixes/inferFromUsage.ts | 24 ++++++------------- .../fourslash/annotateWithTypeFromJSDoc11.ts | 1 + .../fourslash/annotateWithTypeFromJSDoc16.ts | 2 +- .../fourslash/annotateWithTypeFromJSDoc9.5.ts | 1 + .../fourslash/annotateWithTypeFromJSDoc9.ts | 1 + .../codeFixAwaitInSyncFunction6.5.ts | 1 + .../fourslash/codeFixAwaitInSyncFunction6.ts | 1 + ...aceComputedPropertyNameWellKnownSymbols.ts | 1 + .../fourslash/codeFixInferFromUsageArrow.ts | 15 ++++++++++++ 9 files changed, 29 insertions(+), 18 deletions(-) create mode 100644 tests/cases/fourslash/codeFixInferFromUsageArrow.ts diff --git a/src/services/codefixes/inferFromUsage.ts b/src/services/codefixes/inferFromUsage.ts index 9cf5133d982..8e5b962116d 100644 --- a/src/services/codefixes/inferFromUsage.ts +++ b/src/services/codefixes/inferFromUsage.ts @@ -187,24 +187,10 @@ namespace ts.codefix { } } - function isApplicableFunctionForInference(declaration: FunctionLike): declaration is MethodDeclaration | FunctionDeclaration | ConstructorDeclaration { - switch (declaration.kind) { - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.Constructor: - return true; - case SyntaxKind.FunctionExpression: - const parent = declaration.parent; - return isVariableDeclaration(parent) && isIdentifier(parent.name) || !!declaration.name; - } - return false; - } - function annotateParameters(changes: textChanges.ChangeTracker, sourceFile: SourceFile, parameterDeclaration: ParameterDeclaration, containingFunction: FunctionLike, program: Program, host: LanguageServiceHost, cancellationToken: CancellationToken): void { - if (!isIdentifier(parameterDeclaration.name) || !isApplicableFunctionForInference(containingFunction)) { + if (!isIdentifier(parameterDeclaration.name)) { return; } - const parameterInferences = inferTypeForParametersFromUsage(containingFunction, sourceFile, program, cancellationToken) || containingFunction.parameters.map(p => ({ declaration: p, @@ -216,11 +202,14 @@ namespace ts.codefix { annotateJSDocParameters(changes, sourceFile, parameterInferences, program, host); } else { + const needParens = isArrowFunction(containingFunction) && !findChildOfKind(containingFunction, SyntaxKind.OpenParenToken, sourceFile); + if (needParens) changes.insertNodeBefore(sourceFile, first(containingFunction.parameters), createToken(SyntaxKind.OpenParenToken)); for (const { declaration, type } of parameterInferences) { if (declaration && !declaration.type && !declaration.initializer) { annotate(changes, sourceFile, declaration, type, program, host); } } + if (needParens) changes.insertNodeAfter(sourceFile, last(containingFunction.parameters), createToken(SyntaxKind.CloseParenToken)); } } @@ -342,12 +331,13 @@ namespace ts.codefix { return InferFromReference.unifyFromContext(types, checker); } - function inferTypeForParametersFromUsage(containingFunction: FunctionLikeDeclaration, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): ParameterInference[] | undefined { + function inferTypeForParametersFromUsage(containingFunction: FunctionLike, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): ParameterInference[] | undefined { let searchToken; switch (containingFunction.kind) { case SyntaxKind.Constructor: searchToken = findChildOfKind>(containingFunction, SyntaxKind.ConstructorKeyword, sourceFile); break; + case SyntaxKind.ArrowFunction: case SyntaxKind.FunctionExpression: const parent = containingFunction.parent; searchToken = isVariableDeclaration(parent) && isIdentifier(parent.name) ? @@ -399,7 +389,7 @@ namespace ts.codefix { return inferFromContext(usageContext, checker); } - export function inferTypeForParametersFromReferences(references: ReadonlyArray, declaration: FunctionLikeDeclaration, program: Program, cancellationToken: CancellationToken): ParameterInference[] | undefined { + export function inferTypeForParametersFromReferences(references: ReadonlyArray, declaration: FunctionLike, program: Program, cancellationToken: CancellationToken): ParameterInference[] | undefined { const checker = program.getTypeChecker(); if (references.length === 0) { return undefined; diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc11.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc11.ts index 2bd1df5c6e6..a08906ef91a 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc11.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc11.ts @@ -7,6 +7,7 @@ ////var f = (x) => x verify.codeFix({ + index: 0, description: "Annotate with type from JSDoc", newFileContent: `/** diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc16.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc16.ts index 6eb079f9692..eee556aafbd 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc16.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc16.ts @@ -4,8 +4,8 @@ ////var x = (x, ys, ...zs) => { x; ys; zs; }; verify.codeFix({ + index: 3, description: "Annotate with type from JSDoc", - index: 0, newFileContent: `/** @type {function(*, ...number, ...boolean): void} */ var x: (arg0: any, arg1: number[], ...rest: boolean[]) => void = (x, ys, ...zs) => { x; ys; zs; };`, diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc9.5.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc9.5.ts index d0319bee614..aa17b0591c8 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc9.5.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc9.5.ts @@ -8,6 +8,7 @@ ////var f = /*a*/x/*b*/ => x verify.codeFix({ + index: 0, description: "Annotate with type from JSDoc", newFileContent: `/** diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc9.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc9.ts index d1517a2c009..f664d5be6b7 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc9.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc9.ts @@ -7,6 +7,7 @@ ////var f = x => x verify.codeFix({ + index: 0, description: "Annotate with type from JSDoc", newFileContent: `/** diff --git a/tests/cases/fourslash/codeFixAwaitInSyncFunction6.5.ts b/tests/cases/fourslash/codeFixAwaitInSyncFunction6.5.ts index c1b06811113..b33d50078ef 100644 --- a/tests/cases/fourslash/codeFixAwaitInSyncFunction6.5.ts +++ b/tests/cases/fourslash/codeFixAwaitInSyncFunction6.5.ts @@ -5,6 +5,7 @@ ////} verify.codeFix({ + index: 0, description: "Add async modifier to containing function", newFileContent: `const f = async promise => { diff --git a/tests/cases/fourslash/codeFixAwaitInSyncFunction6.ts b/tests/cases/fourslash/codeFixAwaitInSyncFunction6.ts index 0b0aa098164..5fc50a4f9d7 100644 --- a/tests/cases/fourslash/codeFixAwaitInSyncFunction6.ts +++ b/tests/cases/fourslash/codeFixAwaitInSyncFunction6.ts @@ -5,6 +5,7 @@ ////} verify.codeFix({ + index: 0, description: "Add async modifier to containing function", newFileContent: `const f = async (promise) => { diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts index 2bc7a738258..d87fcb73d4e 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts @@ -20,6 +20,7 @@ ////class C implements I {} verify.codeFix({ + index: 0, description: "Implement interface 'I'", newFileContent: `interface I { diff --git a/tests/cases/fourslash/codeFixInferFromUsageArrow.ts b/tests/cases/fourslash/codeFixInferFromUsageArrow.ts new file mode 100644 index 00000000000..6e8cdb2dca5 --- /dev/null +++ b/tests/cases/fourslash/codeFixInferFromUsageArrow.ts @@ -0,0 +1,15 @@ +/// +////const a = (x) => x; +////const b = x => x; +////const c = x => x + 1; +////const d = x => x; +////d(1); +verify.codeFixAll({ + fixId: "inferFromUsage", + fixAllDescription: "Infer all types from usage", + newFileContent: `const a = (x: any) => x; +const b = (x: any) => x; +const c = (x: number) => x + 1; +const d = (x: number) => x; +d(1);`, +});