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.
This commit is contained in:
Nathan Shively-Sanders
2018-12-04 09:03:08 -08:00
committed by GitHub
parent 2103ed69e6
commit 594430f113
9 changed files with 29 additions and 18 deletions

View File

@@ -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<ParameterInference>(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<Token<SyntaxKind.ConstructorKeyword>>(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<Identifier>, declaration: FunctionLikeDeclaration, program: Program, cancellationToken: CancellationToken): ParameterInference[] | undefined {
export function inferTypeForParametersFromReferences(references: ReadonlyArray<Identifier>, declaration: FunctionLike, program: Program, cancellationToken: CancellationToken): ParameterInference[] | undefined {
const checker = program.getTypeChecker();
if (references.length === 0) {
return undefined;

View File

@@ -7,6 +7,7 @@
////var f = (x) => x
verify.codeFix({
index: 0,
description: "Annotate with type from JSDoc",
newFileContent:
`/**

View File

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

View File

@@ -8,6 +8,7 @@
////var f = /*a*/x/*b*/ => x
verify.codeFix({
index: 0,
description: "Annotate with type from JSDoc",
newFileContent:
`/**

View File

@@ -7,6 +7,7 @@
////var f = x => x
verify.codeFix({
index: 0,
description: "Annotate with type from JSDoc",
newFileContent:
`/**

View File

@@ -5,6 +5,7 @@
////}
verify.codeFix({
index: 0,
description: "Add async modifier to containing function",
newFileContent:
`const f = async promise => {

View File

@@ -5,6 +5,7 @@
////}
verify.codeFix({
index: 0,
description: "Add async modifier to containing function",
newFileContent:
`const f = async (promise) => {

View File

@@ -20,6 +20,7 @@
////class C implements I<number> {}
verify.codeFix({
index: 0,
description: "Implement interface 'I<number>'",
newFileContent:
`interface I<Species> {

View File

@@ -0,0 +1,15 @@
/// <reference path='fourslash.ts' />
////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);`,
});