Skip to content

Commit 4cb210c

Browse files
authored
Parameters infer from body and call sites (#28342)
* Parameters infer from body usage and call sites * Function expressions infer from variable decl usages If the function expression is the initialiser of a variable declaration. * Update isApplicableFunctionForInference too * Update baseline
1 parent eba83f4 commit 4cb210c

File tree

5 files changed

+53
-20
lines changed

5 files changed

+53
-20
lines changed

src/services/codefixes/inferFromUsage.ts

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,8 @@ namespace ts.codefix {
194194
case SyntaxKind.Constructor:
195195
return true;
196196
case SyntaxKind.FunctionExpression:
197-
return !!declaration.name;
197+
const parent = declaration.parent;
198+
return isVariableDeclaration(parent) && isIdentifier(parent.name) || !!declaration.name;
198199
}
199200
return false;
200201
}
@@ -335,22 +336,31 @@ namespace ts.codefix {
335336
}
336337

337338
function inferTypeForVariableFromUsage(token: Identifier, program: Program, cancellationToken: CancellationToken): Type {
338-
return InferFromReference.inferTypeFromReferences(getReferences(token, program, cancellationToken), program.getTypeChecker(), cancellationToken);
339+
const references = getReferences(token, program, cancellationToken);
340+
const checker = program.getTypeChecker();
341+
const types = InferFromReference.inferTypesFromReferences(references, checker, cancellationToken);
342+
return InferFromReference.unifyFromContext(types, checker);
339343
}
340344

341345
function inferTypeForParametersFromUsage(containingFunction: FunctionLikeDeclaration, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): ParameterInference[] | undefined {
346+
let searchToken;
342347
switch (containingFunction.kind) {
343348
case SyntaxKind.Constructor:
349+
searchToken = findChildOfKind<Token<SyntaxKind.ConstructorKeyword>>(containingFunction, SyntaxKind.ConstructorKeyword, sourceFile);
350+
break;
344351
case SyntaxKind.FunctionExpression:
352+
const parent = containingFunction.parent;
353+
searchToken = isVariableDeclaration(parent) && isIdentifier(parent.name) ?
354+
parent.name :
355+
containingFunction.name;
356+
break;
345357
case SyntaxKind.FunctionDeclaration:
346358
case SyntaxKind.MethodDeclaration:
347-
const isConstructor = containingFunction.kind === SyntaxKind.Constructor;
348-
const searchToken = isConstructor ?
349-
findChildOfKind<Token<SyntaxKind.ConstructorKeyword>>(containingFunction, SyntaxKind.ConstructorKeyword, sourceFile) :
350-
containingFunction.name;
351-
if (searchToken) {
352-
return InferFromReference.inferTypeForParametersFromReferences(getReferences(searchToken, program, cancellationToken), containingFunction, program, cancellationToken);
353-
}
359+
searchToken = containingFunction.name;
360+
break;
361+
}
362+
if (searchToken) {
363+
return InferFromReference.inferTypeForParametersFromReferences(getReferences(searchToken, program, cancellationToken), containingFunction, program, cancellationToken);
354364
}
355365
}
356366

@@ -380,21 +390,20 @@ namespace ts.codefix {
380390
stringIndexContext?: UsageContext;
381391
}
382392

383-
export function inferTypeFromReferences(references: ReadonlyArray<Identifier>, checker: TypeChecker, cancellationToken: CancellationToken): Type {
393+
export function inferTypesFromReferences(references: ReadonlyArray<Identifier>, checker: TypeChecker, cancellationToken: CancellationToken): Type[] {
384394
const usageContext: UsageContext = {};
385395
for (const reference of references) {
386396
cancellationToken.throwIfCancellationRequested();
387397
inferTypeFromContext(reference, checker, usageContext);
388398
}
389-
return unifyFromContext(inferFromContext(usageContext, checker), checker);
399+
return inferFromContext(usageContext, checker);
390400
}
391401

392402
export function inferTypeForParametersFromReferences(references: ReadonlyArray<Identifier>, declaration: FunctionLikeDeclaration, program: Program, cancellationToken: CancellationToken): ParameterInference[] | undefined {
393403
const checker = program.getTypeChecker();
394404
if (references.length === 0) {
395405
return undefined;
396406
}
397-
398407
if (!declaration.parameters) {
399408
return undefined;
400409
}
@@ -414,10 +423,8 @@ namespace ts.codefix {
414423
if (callContext.argumentTypes.length <= parameterIndex) {
415424
isOptional = isInJSFile(declaration);
416425
types.push(checker.getUndefinedType());
417-
continue;
418426
}
419-
420-
if (isRest) {
427+
else if (isRest) {
421428
for (let i = parameterIndex; i < callContext.argumentTypes.length; i++) {
422429
types.push(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[i]));
423430
}
@@ -426,10 +433,10 @@ namespace ts.codefix {
426433
types.push(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[parameterIndex]));
427434
}
428435
}
429-
let type = unifyFromContext(types, checker);
430-
if (type.flags & TypeFlags.Any && isIdentifier(parameter.name)) {
431-
type = inferTypeForVariableFromUsage(parameter.name, program, cancellationToken);
436+
if (isIdentifier(parameter.name)) {
437+
types.push(...inferTypesFromReferences(getReferences(parameter.name, program, cancellationToken), checker, cancellationToken));
432438
}
439+
const type = unifyFromContext(types, checker);
433440
return {
434441
type: isRest ? checker.createArrayType(type) : type,
435442
isOptional: isOptional && !isRest,
@@ -667,7 +674,7 @@ namespace ts.codefix {
667674
}
668675
}
669676

670-
function unifyFromContext(inferences: ReadonlyArray<Type>, checker: TypeChecker, fallback = checker.getAnyType()): Type {
677+
export function unifyFromContext(inferences: ReadonlyArray<Type>, checker: TypeChecker, fallback = checker.getAnyType()): Type {
671678
if (!inferences.length) return fallback;
672679
const hasNonVacuousType = inferences.some(i => !(i.flags & (TypeFlags.Any | TypeFlags.Void)));
673680
const hasNonVacuousNonAnonymousType = inferences.some(

tests/cases/fourslash/annotateWithTypeFromJSDoc8.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
verify.codeFix({
1212
description: "Annotate with type from JSDoc",
13+
index: 0,
1314
newFileContent:
1415
`/**
1516
* @param {number} x
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////class C {
4+
////
5+
////}
6+
////var c = new C()
7+
////function f([|x, y |]) {
8+
//// if (y) {
9+
//// x = 1
10+
//// }
11+
//// return x
12+
////}
13+
////f(new C())
14+
15+
16+
verify.rangeAfterCodeFix("x: number | C, y: undefined",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////var f = function ([|x |]) {
4+
//// return x
5+
////}
6+
////f(1)
7+
8+
verify.rangeAfterCodeFix("x: number",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0);
9+

tests/cases/fourslash/codeFixInferFromUsageUnifyAnonymousType.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@
1616
////kw("6", { beforeExpr: true, prefix: true, startsExpr: true })
1717

1818

19-
verify.rangeAfterCodeFix("name: string, options: { startsExpr?: boolean; beforeExpr?: boolean; isLoop?: boolean; prefix?: boolean; } | undefined",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0);
19+
verify.rangeAfterCodeFix("name: string | number, options: { startsExpr?: boolean; beforeExpr?: boolean; isLoop?: boolean; prefix?: boolean; keyword?: any; } | undefined",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0);

0 commit comments

Comments
 (0)