mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-15 04:43:37 -05:00
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
This commit is contained in:
committed by
GitHub
parent
eba83f4ea7
commit
4cb210ce2e
@@ -194,7 +194,8 @@ namespace ts.codefix {
|
||||
case SyntaxKind.Constructor:
|
||||
return true;
|
||||
case SyntaxKind.FunctionExpression:
|
||||
return !!declaration.name;
|
||||
const parent = declaration.parent;
|
||||
return isVariableDeclaration(parent) && isIdentifier(parent.name) || !!declaration.name;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -335,22 +336,31 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function inferTypeForVariableFromUsage(token: Identifier, program: Program, cancellationToken: CancellationToken): Type {
|
||||
return InferFromReference.inferTypeFromReferences(getReferences(token, program, cancellationToken), program.getTypeChecker(), cancellationToken);
|
||||
const references = getReferences(token, program, cancellationToken);
|
||||
const checker = program.getTypeChecker();
|
||||
const types = InferFromReference.inferTypesFromReferences(references, checker, cancellationToken);
|
||||
return InferFromReference.unifyFromContext(types, checker);
|
||||
}
|
||||
|
||||
function inferTypeForParametersFromUsage(containingFunction: FunctionLikeDeclaration, 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.FunctionExpression:
|
||||
const parent = containingFunction.parent;
|
||||
searchToken = isVariableDeclaration(parent) && isIdentifier(parent.name) ?
|
||||
parent.name :
|
||||
containingFunction.name;
|
||||
break;
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
const isConstructor = containingFunction.kind === SyntaxKind.Constructor;
|
||||
const searchToken = isConstructor ?
|
||||
findChildOfKind<Token<SyntaxKind.ConstructorKeyword>>(containingFunction, SyntaxKind.ConstructorKeyword, sourceFile) :
|
||||
containingFunction.name;
|
||||
if (searchToken) {
|
||||
return InferFromReference.inferTypeForParametersFromReferences(getReferences(searchToken, program, cancellationToken), containingFunction, program, cancellationToken);
|
||||
}
|
||||
searchToken = containingFunction.name;
|
||||
break;
|
||||
}
|
||||
if (searchToken) {
|
||||
return InferFromReference.inferTypeForParametersFromReferences(getReferences(searchToken, program, cancellationToken), containingFunction, program, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,13 +390,13 @@ namespace ts.codefix {
|
||||
stringIndexContext?: UsageContext;
|
||||
}
|
||||
|
||||
export function inferTypeFromReferences(references: ReadonlyArray<Identifier>, checker: TypeChecker, cancellationToken: CancellationToken): Type {
|
||||
export function inferTypesFromReferences(references: ReadonlyArray<Identifier>, checker: TypeChecker, cancellationToken: CancellationToken): Type[] {
|
||||
const usageContext: UsageContext = {};
|
||||
for (const reference of references) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
inferTypeFromContext(reference, checker, usageContext);
|
||||
}
|
||||
return unifyFromContext(inferFromContext(usageContext, checker), checker);
|
||||
return inferFromContext(usageContext, checker);
|
||||
}
|
||||
|
||||
export function inferTypeForParametersFromReferences(references: ReadonlyArray<Identifier>, declaration: FunctionLikeDeclaration, program: Program, cancellationToken: CancellationToken): ParameterInference[] | undefined {
|
||||
@@ -394,7 +404,6 @@ namespace ts.codefix {
|
||||
if (references.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!declaration.parameters) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -414,10 +423,8 @@ namespace ts.codefix {
|
||||
if (callContext.argumentTypes.length <= parameterIndex) {
|
||||
isOptional = isInJSFile(declaration);
|
||||
types.push(checker.getUndefinedType());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isRest) {
|
||||
else if (isRest) {
|
||||
for (let i = parameterIndex; i < callContext.argumentTypes.length; i++) {
|
||||
types.push(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[i]));
|
||||
}
|
||||
@@ -426,10 +433,10 @@ namespace ts.codefix {
|
||||
types.push(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[parameterIndex]));
|
||||
}
|
||||
}
|
||||
let type = unifyFromContext(types, checker);
|
||||
if (type.flags & TypeFlags.Any && isIdentifier(parameter.name)) {
|
||||
type = inferTypeForVariableFromUsage(parameter.name, program, cancellationToken);
|
||||
if (isIdentifier(parameter.name)) {
|
||||
types.push(...inferTypesFromReferences(getReferences(parameter.name, program, cancellationToken), checker, cancellationToken));
|
||||
}
|
||||
const type = unifyFromContext(types, checker);
|
||||
return {
|
||||
type: isRest ? checker.createArrayType(type) : type,
|
||||
isOptional: isOptional && !isRest,
|
||||
@@ -667,7 +674,7 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
|
||||
function unifyFromContext(inferences: ReadonlyArray<Type>, checker: TypeChecker, fallback = checker.getAnyType()): Type {
|
||||
export function unifyFromContext(inferences: ReadonlyArray<Type>, checker: TypeChecker, fallback = checker.getAnyType()): Type {
|
||||
if (!inferences.length) return fallback;
|
||||
const hasNonVacuousType = inferences.some(i => !(i.flags & (TypeFlags.Any | TypeFlags.Void)));
|
||||
const hasNonVacuousNonAnonymousType = inferences.some(
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
verify.codeFix({
|
||||
description: "Annotate with type from JSDoc",
|
||||
index: 0,
|
||||
newFileContent:
|
||||
`/**
|
||||
* @param {number} x
|
||||
|
||||
16
tests/cases/fourslash/codeFixInferFromUsageCallBodyBoth.ts
Normal file
16
tests/cases/fourslash/codeFixInferFromUsageCallBodyBoth.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////class C {
|
||||
////
|
||||
////}
|
||||
////var c = new C()
|
||||
////function f([|x, y |]) {
|
||||
//// if (y) {
|
||||
//// x = 1
|
||||
//// }
|
||||
//// return x
|
||||
////}
|
||||
////f(new C())
|
||||
|
||||
|
||||
verify.rangeAfterCodeFix("x: number | C, y: undefined",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0);
|
||||
@@ -0,0 +1,9 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////var f = function ([|x |]) {
|
||||
//// return x
|
||||
////}
|
||||
////f(1)
|
||||
|
||||
verify.rangeAfterCodeFix("x: number",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0);
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
////kw("6", { beforeExpr: true, prefix: true, startsExpr: true })
|
||||
|
||||
|
||||
verify.rangeAfterCodeFix("name: string, options: { startsExpr?: boolean; beforeExpr?: boolean; isLoop?: boolean; prefix?: boolean; } | undefined",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0);
|
||||
verify.rangeAfterCodeFix("name: string | number, options: { startsExpr?: boolean; beforeExpr?: boolean; isLoop?: boolean; prefix?: boolean; keyword?: any; } | undefined",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0);
|
||||
|
||||
Reference in New Issue
Block a user