• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* @internal */
2namespace ts.codefix {
3    const fixId = "inferFromUsage";
4    const errorCodes = [
5        // Variable declarations
6        Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined.code,
7
8        // Variable uses
9        Diagnostics.Variable_0_implicitly_has_an_1_type.code,
10
11        // Parameter declarations
12        Diagnostics.Parameter_0_implicitly_has_an_1_type.code,
13        Diagnostics.Rest_parameter_0_implicitly_has_an_any_type.code,
14
15        // Get Accessor declarations
16        Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation.code,
17        Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type.code,
18
19        // Set Accessor declarations
20        Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation.code,
21
22        // Property declarations
23        Diagnostics.Member_0_implicitly_has_an_1_type.code,
24
25        //// Suggestions
26        // Variable declarations
27        Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_but_a_better_type_may_be_inferred_from_usage.code,
28
29        // Variable uses
30        Diagnostics.Variable_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage.code,
31
32        // Parameter declarations
33        Diagnostics.Parameter_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage.code,
34        Diagnostics.Rest_parameter_0_implicitly_has_an_any_type_but_a_better_type_may_be_inferred_from_usage.code,
35
36        // Get Accessor declarations
37        Diagnostics.Property_0_implicitly_has_type_any_but_a_better_type_for_its_get_accessor_may_be_inferred_from_usage.code,
38        Diagnostics._0_implicitly_has_an_1_return_type_but_a_better_type_may_be_inferred_from_usage.code,
39
40        // Set Accessor declarations
41        Diagnostics.Property_0_implicitly_has_type_any_but_a_better_type_for_its_set_accessor_may_be_inferred_from_usage.code,
42
43        // Property declarations
44        Diagnostics.Member_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage.code,
45
46        // Function expressions and declarations
47        Diagnostics.this_implicitly_has_type_any_because_it_does_not_have_a_type_annotation.code,
48    ];
49    registerCodeFix({
50        errorCodes,
51        getCodeActions(context) {
52            const { sourceFile, program, span: { start }, errorCode, cancellationToken, host, preferences } = context;
53
54            const token = getTokenAtPosition(sourceFile, start);
55            let declaration: Declaration | undefined;
56            const changes = textChanges.ChangeTracker.with(context, changes => {
57                declaration = doChange(changes, sourceFile, token, errorCode, program, cancellationToken, /*markSeen*/ returnTrue, host, preferences);
58            });
59            const name = declaration && getNameOfDeclaration(declaration);
60            return !name || changes.length === 0 ? undefined
61                : [createCodeFixAction(fixId, changes, [getDiagnostic(errorCode, token), getTextOfNode(name)], fixId, Diagnostics.Infer_all_types_from_usage)];
62        },
63        fixIds: [fixId],
64        getAllCodeActions(context) {
65            const { sourceFile, program, cancellationToken, host, preferences } = context;
66            const markSeen = nodeSeenTracker();
67            return codeFixAll(context, errorCodes, (changes, err) => {
68                doChange(changes, sourceFile, getTokenAtPosition(err.file, err.start), err.code, program, cancellationToken, markSeen, host, preferences);
69            });
70        },
71    });
72
73    function getDiagnostic(errorCode: number, token: Node): DiagnosticMessage {
74        switch (errorCode) {
75            case Diagnostics.Parameter_0_implicitly_has_an_1_type.code:
76            case Diagnostics.Parameter_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage.code:
77                return isSetAccessorDeclaration(getContainingFunction(token)!) ? Diagnostics.Infer_type_of_0_from_usage : Diagnostics.Infer_parameter_types_from_usage; // TODO: GH#18217
78            case Diagnostics.Rest_parameter_0_implicitly_has_an_any_type.code:
79            case Diagnostics.Rest_parameter_0_implicitly_has_an_any_type_but_a_better_type_may_be_inferred_from_usage.code:
80                return Diagnostics.Infer_parameter_types_from_usage;
81            case Diagnostics.this_implicitly_has_type_any_because_it_does_not_have_a_type_annotation.code:
82                return Diagnostics.Infer_this_type_of_0_from_usage;
83            default:
84                return Diagnostics.Infer_type_of_0_from_usage;
85        }
86    }
87
88    /** Map suggestion code to error code */
89    function mapSuggestionDiagnostic(errorCode: number) {
90        switch (errorCode) {
91            case Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_but_a_better_type_may_be_inferred_from_usage.code:
92                return Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined.code;
93            case Diagnostics.Variable_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage.code:
94                return Diagnostics.Variable_0_implicitly_has_an_1_type.code;
95            case Diagnostics.Parameter_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage.code:
96                return Diagnostics.Parameter_0_implicitly_has_an_1_type.code;
97            case Diagnostics.Rest_parameter_0_implicitly_has_an_any_type_but_a_better_type_may_be_inferred_from_usage.code:
98                return Diagnostics.Rest_parameter_0_implicitly_has_an_any_type.code;
99            case Diagnostics.Property_0_implicitly_has_type_any_but_a_better_type_for_its_get_accessor_may_be_inferred_from_usage.code:
100                return Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation.code;
101            case Diagnostics._0_implicitly_has_an_1_return_type_but_a_better_type_may_be_inferred_from_usage.code:
102                return Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type.code;
103            case Diagnostics.Property_0_implicitly_has_type_any_but_a_better_type_for_its_set_accessor_may_be_inferred_from_usage.code:
104                return Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation.code;
105            case Diagnostics.Member_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage.code:
106                return Diagnostics.Member_0_implicitly_has_an_1_type.code;
107        }
108        return errorCode;
109    }
110
111    function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Node, errorCode: number, program: Program, cancellationToken: CancellationToken, markSeen: NodeSeenTracker, host: LanguageServiceHost, preferences: UserPreferences): Declaration | undefined {
112        if (!isParameterPropertyModifier(token.kind) && token.kind !== SyntaxKind.Identifier && token.kind !== SyntaxKind.DotDotDotToken && token.kind !== SyntaxKind.ThisKeyword) {
113            return undefined;
114        }
115
116        const { parent } = token;
117        const importAdder = createImportAdder(sourceFile, program, preferences, host);
118        errorCode = mapSuggestionDiagnostic(errorCode);
119        switch (errorCode) {
120            // Variable and Property declarations
121            case Diagnostics.Member_0_implicitly_has_an_1_type.code:
122            case Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined.code:
123                if ((isVariableDeclaration(parent) && markSeen(parent)) || isPropertyDeclaration(parent) || isPropertySignature(parent)) { // handle bad location
124                    annotateVariableDeclaration(changes, importAdder, sourceFile, parent, program, host, cancellationToken);
125                    importAdder.writeFixes(changes);
126                    return parent;
127                }
128                if (isPropertyAccessExpression(parent)) {
129                    const type = inferTypeForVariableFromUsage(parent.name, program, cancellationToken);
130                    const typeNode = getTypeNodeIfAccessible(type, parent, program, host);
131                    if (typeNode) {
132                        // Note that the codefix will never fire with an existing `@type` tag, so there is no need to merge tags
133                        const typeTag = factory.createJSDocTypeTag(/*tagName*/ undefined, factory.createJSDocTypeExpression(typeNode), /*comment*/ undefined);
134                        changes.addJSDocTags(sourceFile, cast(parent.parent.parent, isExpressionStatement), [typeTag]);
135                    }
136                    importAdder.writeFixes(changes);
137                    return parent;
138                }
139                return undefined;
140
141            case Diagnostics.Variable_0_implicitly_has_an_1_type.code: {
142                const symbol = program.getTypeChecker().getSymbolAtLocation(token);
143                if (symbol && symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) && markSeen(symbol.valueDeclaration)) {
144                    annotateVariableDeclaration(changes, importAdder, getSourceFileOfNode(symbol.valueDeclaration), symbol.valueDeclaration, program, host, cancellationToken);
145                    importAdder.writeFixes(changes);
146                    return symbol.valueDeclaration;
147                }
148                return undefined;
149            }
150        }
151
152        const containingFunction = getContainingFunction(token);
153        if (containingFunction === undefined) {
154            return undefined;
155        }
156
157        let declaration: Declaration | undefined;
158        switch (errorCode) {
159            // Parameter declarations
160            case Diagnostics.Parameter_0_implicitly_has_an_1_type.code:
161                if (isSetAccessorDeclaration(containingFunction)) {
162                    annotateSetAccessor(changes, importAdder, sourceFile, containingFunction, program, host, cancellationToken);
163                    declaration = containingFunction;
164                    break;
165                }
166                // falls through
167            case Diagnostics.Rest_parameter_0_implicitly_has_an_any_type.code:
168                if (markSeen(containingFunction)) {
169                    const param = cast(parent, isParameter);
170                    annotateParameters(changes, importAdder, sourceFile, param, containingFunction, program, host, cancellationToken);
171                    declaration = param;
172                }
173                break;
174
175            // Get Accessor declarations
176            case Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation.code:
177            case Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type.code:
178                if (isGetAccessorDeclaration(containingFunction) && isIdentifier(containingFunction.name)) {
179                    annotate(changes, importAdder, sourceFile, containingFunction, inferTypeForVariableFromUsage(containingFunction.name, program, cancellationToken), program, host);
180                    declaration = containingFunction;
181                }
182                break;
183
184            // Set Accessor declarations
185            case Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation.code:
186                if (isSetAccessorDeclaration(containingFunction)) {
187                    annotateSetAccessor(changes, importAdder, sourceFile, containingFunction, program, host, cancellationToken);
188                    declaration = containingFunction;
189                }
190                break;
191
192            // Function 'this'
193            case Diagnostics.this_implicitly_has_type_any_because_it_does_not_have_a_type_annotation.code:
194                if (textChanges.isThisTypeAnnotatable(containingFunction) && markSeen(containingFunction)) {
195                    annotateThis(changes, sourceFile, containingFunction, program, host, cancellationToken);
196                    declaration = containingFunction;
197                }
198                break;
199
200            default:
201                return Debug.fail(String(errorCode));
202        }
203
204        importAdder.writeFixes(changes);
205        return declaration;
206    }
207
208    function annotateVariableDeclaration(
209        changes: textChanges.ChangeTracker,
210        importAdder: ImportAdder,
211        sourceFile: SourceFile,
212        declaration: VariableDeclaration | PropertyDeclaration | PropertySignature,
213        program: Program,
214        host: LanguageServiceHost,
215        cancellationToken: CancellationToken,
216    ): void {
217        if (isIdentifier(declaration.name)) {
218            annotate(changes, importAdder, sourceFile, declaration, inferTypeForVariableFromUsage(declaration.name, program, cancellationToken), program, host);
219        }
220    }
221
222    function annotateParameters(
223        changes: textChanges.ChangeTracker,
224        importAdder: ImportAdder,
225        sourceFile: SourceFile,
226        parameterDeclaration: ParameterDeclaration,
227        containingFunction: SignatureDeclaration,
228        program: Program,
229        host: LanguageServiceHost,
230        cancellationToken: CancellationToken,
231    ): void {
232        if (!isIdentifier(parameterDeclaration.name)) {
233            return;
234        }
235
236        const parameterInferences = inferTypeForParametersFromUsage(containingFunction, sourceFile, program, cancellationToken);
237        Debug.assert(containingFunction.parameters.length === parameterInferences.length, "Parameter count and inference count should match");
238
239        if (isInJSFile(containingFunction)) {
240            annotateJSDocParameters(changes, sourceFile, parameterInferences, program, host);
241        }
242        else {
243            const needParens = isArrowFunction(containingFunction) && !findChildOfKind(containingFunction, SyntaxKind.OpenParenToken, sourceFile);
244            if (needParens) changes.insertNodeBefore(sourceFile, first(containingFunction.parameters), factory.createToken(SyntaxKind.OpenParenToken));
245            for (const { declaration, type } of parameterInferences) {
246                if (declaration && !declaration.type && !declaration.initializer) {
247                    annotate(changes, importAdder, sourceFile, declaration, type, program, host);
248                }
249            }
250            if (needParens) changes.insertNodeAfter(sourceFile, last(containingFunction.parameters), factory.createToken(SyntaxKind.CloseParenToken));
251        }
252    }
253
254    function annotateThis(changes: textChanges.ChangeTracker, sourceFile: SourceFile, containingFunction: textChanges.ThisTypeAnnotatable, program: Program, host: LanguageServiceHost, cancellationToken: CancellationToken) {
255        const references = getFunctionReferences(containingFunction, sourceFile, program, cancellationToken);
256        if (!references || !references.length) {
257            return;
258        }
259        const thisInference = inferTypeFromReferences(program, references, cancellationToken).thisParameter();
260        const typeNode = getTypeNodeIfAccessible(thisInference, containingFunction, program, host);
261        if (!typeNode) {
262            return;
263        }
264
265        if (isInJSFile(containingFunction)) {
266            annotateJSDocThis(changes, sourceFile, containingFunction, typeNode);
267        }
268        else {
269            changes.tryInsertThisTypeAnnotation(sourceFile, containingFunction, typeNode);
270        }
271    }
272
273    function annotateJSDocThis(changes: textChanges.ChangeTracker, sourceFile: SourceFile, containingFunction: SignatureDeclaration, typeNode: TypeNode) {
274        changes.addJSDocTags(sourceFile, containingFunction, [
275            factory.createJSDocThisTag(/*tagName*/ undefined, factory.createJSDocTypeExpression(typeNode)),
276        ]);
277    }
278
279    function annotateSetAccessor(
280        changes: textChanges.ChangeTracker,
281        importAdder: ImportAdder,
282        sourceFile: SourceFile,
283        setAccessorDeclaration: SetAccessorDeclaration,
284        program: Program,
285        host: LanguageServiceHost,
286        cancellationToken: CancellationToken,
287
288    ): void {
289        const param = firstOrUndefined(setAccessorDeclaration.parameters);
290        if (param && isIdentifier(setAccessorDeclaration.name) && isIdentifier(param.name)) {
291            let type = inferTypeForVariableFromUsage(setAccessorDeclaration.name, program, cancellationToken);
292            if (type === program.getTypeChecker().getAnyType()) {
293                type = inferTypeForVariableFromUsage(param.name, program, cancellationToken);
294            }
295            if (isInJSFile(setAccessorDeclaration)) {
296                annotateJSDocParameters(changes, sourceFile, [{ declaration: param, type }], program, host);
297            }
298            else {
299                annotate(changes, importAdder, sourceFile, param, type, program, host);
300            }
301        }
302    }
303
304    function annotate(changes: textChanges.ChangeTracker, importAdder: ImportAdder, sourceFile: SourceFile, declaration: textChanges.TypeAnnotatable, type: Type, program: Program, host: LanguageServiceHost): void {
305        const typeNode = getTypeNodeIfAccessible(type, declaration, program, host);
306        if (typeNode) {
307            if (isInJSFile(sourceFile) && declaration.kind !== SyntaxKind.PropertySignature) {
308                const parent = isVariableDeclaration(declaration) ? tryCast(declaration.parent.parent, isVariableStatement) : declaration;
309                if (!parent) {
310                    return;
311                }
312                const typeExpression = factory.createJSDocTypeExpression(typeNode);
313                const typeTag = isGetAccessorDeclaration(declaration) ? factory.createJSDocReturnTag(/*tagName*/ undefined, typeExpression, /*comment*/ undefined) : factory.createJSDocTypeTag(/*tagName*/ undefined, typeExpression, /*comment*/ undefined);
314                changes.addJSDocTags(sourceFile, parent, [typeTag]);
315            }
316            else if (!tryReplaceImportTypeNodeWithAutoImport(typeNode, declaration, sourceFile, changes, importAdder, getEmitScriptTarget(program.getCompilerOptions()))) {
317                changes.tryInsertTypeAnnotation(sourceFile, declaration, typeNode);
318            }
319        }
320    }
321
322    function tryReplaceImportTypeNodeWithAutoImport(
323        typeNode: TypeNode,
324        declaration: textChanges.TypeAnnotatable,
325        sourceFile: SourceFile,
326        changes: textChanges.ChangeTracker,
327        importAdder: ImportAdder,
328        scriptTarget: ScriptTarget
329    ): boolean {
330        const importableReference = tryGetAutoImportableReferenceFromTypeNode(typeNode, scriptTarget);
331        if (importableReference && changes.tryInsertTypeAnnotation(sourceFile, declaration, importableReference.typeNode)) {
332            forEach(importableReference.symbols, s => importAdder.addImportFromExportedSymbol(s, /*usageIsTypeOnly*/ true));
333            return true;
334        }
335        return false;
336    }
337
338    function annotateJSDocParameters(changes: textChanges.ChangeTracker, sourceFile: SourceFile, parameterInferences: readonly ParameterInference[], program: Program, host: LanguageServiceHost): void {
339        const signature = parameterInferences.length && parameterInferences[0].declaration.parent;
340        if (!signature) {
341            return;
342        }
343
344        const inferences = mapDefined(parameterInferences, inference => {
345            const param = inference.declaration;
346            // only infer parameters that have (1) no type and (2) an accessible inferred type
347            if (param.initializer || getJSDocType(param) || !isIdentifier(param.name)) {
348                return;
349            }
350            const typeNode = inference.type && getTypeNodeIfAccessible(inference.type, param, program, host);
351            if (typeNode) {
352                const name = factory.cloneNode(param.name);
353                setEmitFlags(name, EmitFlags.NoComments | EmitFlags.NoNestedComments);
354                return { name: factory.cloneNode(param.name), param, isOptional: !!inference.isOptional, typeNode };
355            }
356        });
357
358        if (!inferences.length) {
359            return;
360        }
361
362        if (isArrowFunction(signature) || isFunctionExpression(signature)) {
363            const needParens = isArrowFunction(signature) && !findChildOfKind(signature, SyntaxKind.OpenParenToken, sourceFile);
364            if (needParens) {
365                changes.insertNodeBefore(sourceFile, first(signature.parameters), factory.createToken(SyntaxKind.OpenParenToken));
366            }
367
368            forEach(inferences, ({ typeNode, param }) => {
369                const typeTag = factory.createJSDocTypeTag(/*tagName*/ undefined, factory.createJSDocTypeExpression(typeNode));
370                const jsDoc = factory.createJSDocComment(/*comment*/ undefined, [typeTag]);
371                changes.insertNodeAt(sourceFile, param.getStart(sourceFile), jsDoc, { suffix: " " });
372            });
373
374            if (needParens) {
375                changes.insertNodeAfter(sourceFile, last(signature.parameters), factory.createToken(SyntaxKind.CloseParenToken));
376            }
377        }
378        else {
379            const paramTags = map(inferences, ({ name, typeNode, isOptional }) =>
380                factory.createJSDocParameterTag(/*tagName*/ undefined, name, /*isBracketed*/ !!isOptional, factory.createJSDocTypeExpression(typeNode), /* isNameFirst */ false, /*comment*/ undefined));
381            changes.addJSDocTags(sourceFile, signature, paramTags);
382        }
383    }
384
385    function getReferences(token: PropertyName | Token<SyntaxKind.ConstructorKeyword>, program: Program, cancellationToken: CancellationToken): readonly Identifier[] {
386        // Position shouldn't matter since token is not a SourceFile.
387        return mapDefined(FindAllReferences.getReferenceEntriesForNode(-1, token, program, program.getSourceFiles(), cancellationToken), entry =>
388            entry.kind !== FindAllReferences.EntryKind.Span ? tryCast(entry.node, isIdentifier) : undefined);
389    }
390
391    function inferTypeForVariableFromUsage(token: Identifier | PrivateIdentifier, program: Program, cancellationToken: CancellationToken): Type {
392        const references = getReferences(token, program, cancellationToken);
393        return inferTypeFromReferences(program, references, cancellationToken).single();
394    }
395
396    function inferTypeForParametersFromUsage(func: SignatureDeclaration, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken) {
397        const references = getFunctionReferences(func, sourceFile, program, cancellationToken);
398        return references && inferTypeFromReferences(program, references, cancellationToken).parameters(func) ||
399            func.parameters.map<ParameterInference>(p => ({
400                declaration: p,
401                type: isIdentifier(p.name) ? inferTypeForVariableFromUsage(p.name, program, cancellationToken) : program.getTypeChecker().getAnyType()
402            }));
403    }
404
405    function getFunctionReferences(containingFunction: SignatureDeclaration, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): readonly Identifier[] | undefined {
406        let searchToken;
407        switch (containingFunction.kind) {
408            case SyntaxKind.Constructor:
409                searchToken = findChildOfKind<Token<SyntaxKind.ConstructorKeyword>>(containingFunction, SyntaxKind.ConstructorKeyword, sourceFile);
410                break;
411            case SyntaxKind.ArrowFunction:
412            case SyntaxKind.FunctionExpression:
413                const parent = containingFunction.parent;
414                searchToken = (isVariableDeclaration(parent) || isPropertyDeclaration(parent)) && isIdentifier(parent.name) ?
415                    parent.name :
416                    containingFunction.name;
417                break;
418            case SyntaxKind.FunctionDeclaration:
419            case SyntaxKind.MethodDeclaration:
420            case SyntaxKind.MethodSignature:
421                searchToken = containingFunction.name;
422                break;
423        }
424
425        if (!searchToken) {
426            return undefined;
427        }
428
429        return getReferences(searchToken, program, cancellationToken);
430    }
431
432    interface ParameterInference {
433        readonly declaration: ParameterDeclaration;
434        readonly type: Type;
435        readonly isOptional?: boolean;
436    }
437
438    function inferTypeFromReferences(program: Program, references: readonly Identifier[], cancellationToken: CancellationToken) {
439        const checker = program.getTypeChecker();
440        const builtinConstructors: { [s: string]: (t: Type) => Type } = {
441            string: () => checker.getStringType(),
442            number: () => checker.getNumberType(),
443            Array: t => checker.createArrayType(t),
444            Promise: t => checker.createPromiseType(t),
445        };
446        const builtins = [
447            checker.getStringType(),
448            checker.getNumberType(),
449            checker.createArrayType(checker.getAnyType()),
450            checker.createPromiseType(checker.getAnyType()),
451        ];
452
453        return {
454            single,
455            parameters,
456            thisParameter,
457        };
458
459        interface CallUsage {
460            argumentTypes: Type[];
461            return_: Usage;
462        }
463
464        interface Usage {
465            isNumber: boolean | undefined;
466            isString: boolean | undefined;
467            /** Used ambiguously, eg x + ___ or object[___]; results in string | number if no other evidence exists */
468            isNumberOrString: boolean | undefined;
469
470            candidateTypes: Type[] | undefined;
471            properties: UnderscoreEscapedMap<Usage> | undefined;
472            calls: CallUsage[] | undefined;
473            constructs: CallUsage[] | undefined;
474            numberIndex: Usage | undefined;
475            stringIndex: Usage | undefined;
476            candidateThisTypes: Type[] | undefined;
477            inferredTypes: Type[] | undefined;
478        }
479
480        function createEmptyUsage(): Usage {
481            return {
482                isNumber: undefined,
483                isString: undefined,
484                isNumberOrString: undefined,
485                candidateTypes: undefined,
486                properties: undefined,
487                calls: undefined,
488                constructs: undefined,
489                numberIndex: undefined,
490                stringIndex: undefined,
491                candidateThisTypes: undefined,
492                inferredTypes: undefined,
493            };
494        }
495
496        function combineUsages(usages: Usage[]): Usage {
497            const combinedProperties = new Map<__String, Usage[]>();
498            for (const u of usages) {
499                if (u.properties) {
500                    u.properties.forEach((p, name) => {
501                        if (!combinedProperties.has(name)) {
502                            combinedProperties.set(name, []);
503                        }
504                        combinedProperties.get(name)!.push(p);
505                    });
506                }
507            }
508            const properties = new Map<__String, Usage>();
509            combinedProperties.forEach((ps, name) => {
510                properties.set(name, combineUsages(ps));
511            });
512            return {
513                isNumber: usages.some(u => u.isNumber),
514                isString: usages.some(u => u.isString),
515                isNumberOrString: usages.some(u => u.isNumberOrString),
516                candidateTypes: flatMap(usages, u => u.candidateTypes) as Type[],
517                properties,
518                calls: flatMap(usages, u => u.calls) as CallUsage[],
519                constructs: flatMap(usages, u => u.constructs) as CallUsage[],
520                numberIndex: forEach(usages, u => u.numberIndex),
521                stringIndex: forEach(usages, u => u.stringIndex),
522                candidateThisTypes: flatMap(usages, u => u.candidateThisTypes) as Type[],
523                inferredTypes: undefined, // clear type cache
524            };
525        }
526
527        function single(): Type {
528            return combineTypes(inferTypesFromReferencesSingle(references));
529        }
530
531        function parameters(declaration: SignatureDeclaration): ParameterInference[] | undefined {
532            if (references.length === 0 || !declaration.parameters) {
533                return undefined;
534            }
535
536            const usage = createEmptyUsage();
537            for (const reference of references) {
538                cancellationToken.throwIfCancellationRequested();
539                calculateUsageOfNode(reference, usage);
540            }
541            const calls = [...usage.constructs || [], ...usage.calls || []];
542            return declaration.parameters.map((parameter, parameterIndex): ParameterInference => {
543                const types = [];
544                const isRest = isRestParameter(parameter);
545                let isOptional = false;
546                for (const call of calls) {
547                    if (call.argumentTypes.length <= parameterIndex) {
548                        isOptional = isInJSFile(declaration);
549                        types.push(checker.getUndefinedType());
550                    }
551                    else if (isRest) {
552                        for (let i = parameterIndex; i < call.argumentTypes.length; i++) {
553                            types.push(checker.getBaseTypeOfLiteralType(call.argumentTypes[i]));
554                        }
555                    }
556                    else {
557                        types.push(checker.getBaseTypeOfLiteralType(call.argumentTypes[parameterIndex]));
558                    }
559                }
560                if (isIdentifier(parameter.name)) {
561                    const inferred = inferTypesFromReferencesSingle(getReferences(parameter.name, program, cancellationToken));
562                    types.push(...(isRest ? mapDefined(inferred, checker.getElementTypeOfArrayType) : inferred));
563                }
564                const type = combineTypes(types);
565                return {
566                    type: isRest ? checker.createArrayType(type) : type,
567                    isOptional: isOptional && !isRest,
568                    declaration: parameter
569                };
570            });
571        }
572
573        function thisParameter() {
574            const usage = createEmptyUsage();
575            for (const reference of references) {
576                cancellationToken.throwIfCancellationRequested();
577                calculateUsageOfNode(reference, usage);
578            }
579
580            return combineTypes(usage.candidateThisTypes || emptyArray);
581        }
582
583        function inferTypesFromReferencesSingle(references: readonly Identifier[]): Type[] {
584            const usage: Usage = createEmptyUsage();
585            for (const reference of references) {
586                cancellationToken.throwIfCancellationRequested();
587                calculateUsageOfNode(reference, usage);
588            }
589            return inferTypes(usage);
590        }
591
592        function calculateUsageOfNode(node: Expression, usage: Usage): void {
593            while (isRightSideOfQualifiedNameOrPropertyAccess(node)) {
594                node = node.parent as Expression;
595            }
596
597            switch (node.parent.kind) {
598                case SyntaxKind.ExpressionStatement:
599                    inferTypeFromExpressionStatement(node, usage);
600                    break;
601                case SyntaxKind.PostfixUnaryExpression:
602                    usage.isNumber = true;
603                    break;
604                case SyntaxKind.PrefixUnaryExpression:
605                    inferTypeFromPrefixUnaryExpression(node.parent as PrefixUnaryExpression, usage);
606                    break;
607                case SyntaxKind.BinaryExpression:
608                    inferTypeFromBinaryExpression(node, node.parent as BinaryExpression, usage);
609                    break;
610                case SyntaxKind.CaseClause:
611                case SyntaxKind.DefaultClause:
612                    inferTypeFromSwitchStatementLabel(node.parent as CaseOrDefaultClause, usage);
613                    break;
614                case SyntaxKind.CallExpression:
615                case SyntaxKind.NewExpression:
616                    if ((node.parent as CallExpression | NewExpression).expression === node) {
617                        inferTypeFromCallExpression(node.parent as CallExpression | NewExpression, usage);
618                    }
619                    else {
620                        inferTypeFromContextualType(node, usage);
621                    }
622                    break;
623                case SyntaxKind.PropertyAccessExpression:
624                    inferTypeFromPropertyAccessExpression(node.parent as PropertyAccessExpression, usage);
625                    break;
626                case SyntaxKind.ElementAccessExpression:
627                    inferTypeFromPropertyElementExpression(node.parent as ElementAccessExpression, node, usage);
628                    break;
629                case SyntaxKind.PropertyAssignment:
630                case SyntaxKind.ShorthandPropertyAssignment:
631                    inferTypeFromPropertyAssignment(node.parent as PropertyAssignment | ShorthandPropertyAssignment, usage);
632                    break;
633                case SyntaxKind.PropertyDeclaration:
634                    inferTypeFromPropertyDeclaration(node.parent as PropertyDeclaration, usage);
635                    break;
636                case SyntaxKind.VariableDeclaration: {
637                    const { name, initializer } = node.parent as VariableDeclaration;
638                    if (node === name) {
639                        if (initializer) { // This can happen for `let x = null;` which still has an implicit-any error.
640                            addCandidateType(usage, checker.getTypeAtLocation(initializer));
641                        }
642                        break;
643                    }
644                }
645                // falls through
646                default:
647                    return inferTypeFromContextualType(node, usage);
648            }
649        }
650
651        function inferTypeFromContextualType(node: Expression, usage: Usage): void {
652            if (isExpressionNode(node)) {
653                addCandidateType(usage, checker.getContextualType(node));
654            }
655        }
656
657        function inferTypeFromExpressionStatement(node: Expression, usage: Usage): void {
658            addCandidateType(usage, isCallExpression(node) ? checker.getVoidType() : checker.getAnyType());
659        }
660
661        function inferTypeFromPrefixUnaryExpression(node: PrefixUnaryExpression, usage: Usage): void {
662            switch (node.operator) {
663                case SyntaxKind.PlusPlusToken:
664                case SyntaxKind.MinusMinusToken:
665                case SyntaxKind.MinusToken:
666                case SyntaxKind.TildeToken:
667                    usage.isNumber = true;
668                    break;
669
670                case SyntaxKind.PlusToken:
671                    usage.isNumberOrString = true;
672                    break;
673
674                // case SyntaxKind.ExclamationToken:
675                // no inferences here;
676            }
677        }
678
679        function inferTypeFromBinaryExpression(node: Expression, parent: BinaryExpression, usage: Usage): void {
680            switch (parent.operatorToken.kind) {
681                // ExponentiationOperator
682                case SyntaxKind.AsteriskAsteriskToken:
683
684                // MultiplicativeOperator
685                // falls through
686                case SyntaxKind.AsteriskToken:
687                case SyntaxKind.SlashToken:
688                case SyntaxKind.PercentToken:
689
690                // ShiftOperator
691                // falls through
692                case SyntaxKind.LessThanLessThanToken:
693                case SyntaxKind.GreaterThanGreaterThanToken:
694                case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
695
696                // BitwiseOperator
697                // falls through
698                case SyntaxKind.AmpersandToken:
699                case SyntaxKind.BarToken:
700                case SyntaxKind.CaretToken:
701
702                // CompoundAssignmentOperator
703                // falls through
704                case SyntaxKind.MinusEqualsToken:
705                case SyntaxKind.AsteriskAsteriskEqualsToken:
706                case SyntaxKind.AsteriskEqualsToken:
707                case SyntaxKind.SlashEqualsToken:
708                case SyntaxKind.PercentEqualsToken:
709                case SyntaxKind.AmpersandEqualsToken:
710                case SyntaxKind.BarEqualsToken:
711                case SyntaxKind.CaretEqualsToken:
712                case SyntaxKind.LessThanLessThanEqualsToken:
713                case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
714                case SyntaxKind.GreaterThanGreaterThanEqualsToken:
715
716                // AdditiveOperator
717                // falls through
718                case SyntaxKind.MinusToken:
719
720                // RelationalOperator
721                // falls through
722                case SyntaxKind.LessThanToken:
723                case SyntaxKind.LessThanEqualsToken:
724                case SyntaxKind.GreaterThanToken:
725                case SyntaxKind.GreaterThanEqualsToken:
726                    const operandType = checker.getTypeAtLocation(parent.left === node ? parent.right : parent.left);
727                    if (operandType.flags & TypeFlags.EnumLike) {
728                        addCandidateType(usage, operandType);
729                    }
730                    else {
731                        usage.isNumber = true;
732                    }
733                    break;
734
735                case SyntaxKind.PlusEqualsToken:
736                case SyntaxKind.PlusToken:
737                    const otherOperandType = checker.getTypeAtLocation(parent.left === node ? parent.right : parent.left);
738                    if (otherOperandType.flags & TypeFlags.EnumLike) {
739                        addCandidateType(usage, otherOperandType);
740                    }
741                    else if (otherOperandType.flags & TypeFlags.NumberLike) {
742                        usage.isNumber = true;
743                    }
744                    else if (otherOperandType.flags & TypeFlags.StringLike) {
745                        usage.isString = true;
746                    }
747                    else if (otherOperandType.flags & TypeFlags.Any) {
748                        // do nothing, maybe we'll learn something elsewhere
749                    }
750                    else {
751                        usage.isNumberOrString = true;
752                    }
753                    break;
754
755                //  AssignmentOperators
756                case SyntaxKind.EqualsToken:
757                case SyntaxKind.EqualsEqualsToken:
758                case SyntaxKind.EqualsEqualsEqualsToken:
759                case SyntaxKind.ExclamationEqualsEqualsToken:
760                case SyntaxKind.ExclamationEqualsToken:
761                    addCandidateType(usage, checker.getTypeAtLocation(parent.left === node ? parent.right : parent.left));
762                    break;
763
764                case SyntaxKind.InKeyword:
765                    if (node === parent.left) {
766                        usage.isString = true;
767                    }
768                    break;
769
770                // LogicalOperator Or NullishCoalescing
771                case SyntaxKind.BarBarToken:
772                case SyntaxKind.QuestionQuestionToken:
773                    if (node === parent.left &&
774                        (node.parent.parent.kind === SyntaxKind.VariableDeclaration || isAssignmentExpression(node.parent.parent, /*excludeCompoundAssignment*/ true))) {
775                        // var x = x || {};
776                        // TODO: use getFalsyflagsOfType
777                        addCandidateType(usage, checker.getTypeAtLocation(parent.right));
778                    }
779                    break;
780
781                case SyntaxKind.AmpersandAmpersandToken:
782                case SyntaxKind.CommaToken:
783                case SyntaxKind.InstanceOfKeyword:
784                    // nothing to infer here
785                    break;
786            }
787        }
788
789        function inferTypeFromSwitchStatementLabel(parent: CaseOrDefaultClause, usage: Usage): void {
790            addCandidateType(usage, checker.getTypeAtLocation(parent.parent.parent.expression));
791        }
792
793        function inferTypeFromCallExpression(parent: CallExpression | NewExpression, usage: Usage): void {
794            const call: CallUsage = {
795                argumentTypes: [],
796                return_: createEmptyUsage()
797            };
798
799            if (parent.arguments) {
800                for (const argument of parent.arguments) {
801                    call.argumentTypes.push(checker.getTypeAtLocation(argument));
802                }
803            }
804
805            calculateUsageOfNode(parent, call.return_);
806            if (parent.kind === SyntaxKind.CallExpression) {
807                (usage.calls || (usage.calls = [])).push(call);
808            }
809            else {
810                (usage.constructs || (usage.constructs = [])).push(call);
811            }
812        }
813
814        function inferTypeFromPropertyAccessExpression(parent: PropertyAccessExpression, usage: Usage): void {
815            const name = escapeLeadingUnderscores(parent.name.text);
816            if (!usage.properties) {
817                usage.properties = new Map();
818            }
819            const propertyUsage = usage.properties.get(name) || createEmptyUsage();
820            calculateUsageOfNode(parent, propertyUsage);
821            usage.properties.set(name, propertyUsage);
822        }
823
824        function inferTypeFromPropertyElementExpression(parent: ElementAccessExpression, node: Expression, usage: Usage): void {
825            if (node === parent.argumentExpression) {
826                usage.isNumberOrString = true;
827                return;
828            }
829            else {
830                const indexType = checker.getTypeAtLocation(parent.argumentExpression);
831                const indexUsage = createEmptyUsage();
832                calculateUsageOfNode(parent, indexUsage);
833                if (indexType.flags & TypeFlags.NumberLike) {
834                    usage.numberIndex = indexUsage;
835                }
836                else {
837                    usage.stringIndex = indexUsage;
838                }
839            }
840        }
841
842        function inferTypeFromPropertyAssignment(assignment: PropertyAssignment | ShorthandPropertyAssignment, usage: Usage) {
843            const nodeWithRealType = isVariableDeclaration(assignment.parent.parent) ?
844                assignment.parent.parent :
845                assignment.parent;
846            addCandidateThisType(usage, checker.getTypeAtLocation(nodeWithRealType));
847        }
848
849        function inferTypeFromPropertyDeclaration(declaration: PropertyDeclaration, usage: Usage) {
850            addCandidateThisType(usage, checker.getTypeAtLocation(declaration.parent));
851        }
852
853        interface Priority {
854            high: (t: Type) => boolean;
855            low: (t: Type) => boolean;
856        }
857
858        function removeLowPriorityInferences(inferences: readonly Type[], priorities: Priority[]): Type[] {
859            const toRemove: ((t: Type) => boolean)[] = [];
860            for (const i of inferences) {
861                for (const { high, low } of priorities) {
862                    if (high(i)) {
863                        Debug.assert(!low(i), "Priority can't have both low and high");
864                        toRemove.push(low);
865                    }
866                }
867            }
868            return inferences.filter(i => toRemove.every(f => !f(i)));
869        }
870
871        function combineFromUsage(usage: Usage) {
872            return combineTypes(inferTypes(usage));
873        }
874
875        function combineTypes(inferences: readonly Type[]): Type {
876            if (!inferences.length) return checker.getAnyType();
877
878            // 1. string or number individually override string | number
879            // 2. non-any, non-void overrides any or void
880            // 3. non-nullable, non-any, non-void, non-anonymous overrides anonymous types
881            const stringNumber = checker.getUnionType([checker.getStringType(), checker.getNumberType()]);
882            const priorities: Priority[] = [
883                {
884                    high: t => t === checker.getStringType() || t === checker.getNumberType(),
885                    low: t => t === stringNumber
886                },
887                {
888                    high: t => !(t.flags & (TypeFlags.Any | TypeFlags.Void)),
889                    low: t => !!(t.flags & (TypeFlags.Any | TypeFlags.Void))
890                },
891                {
892                    high: t => !(t.flags & (TypeFlags.Nullable | TypeFlags.Any | TypeFlags.Void)) && !(getObjectFlags(t) & ObjectFlags.Anonymous),
893                    low: t => !!(getObjectFlags(t) & ObjectFlags.Anonymous)
894                }];
895            let good = removeLowPriorityInferences(inferences, priorities);
896            const anons = good.filter(i => getObjectFlags(i) & ObjectFlags.Anonymous) as AnonymousType[];
897            if (anons.length) {
898                good = good.filter(i => !(getObjectFlags(i) & ObjectFlags.Anonymous));
899                good.push(combineAnonymousTypes(anons));
900            }
901            return checker.getWidenedType(checker.getUnionType(good.map(checker.getBaseTypeOfLiteralType), UnionReduction.Subtype));
902        }
903
904        function combineAnonymousTypes(anons: AnonymousType[]) {
905            if (anons.length === 1) {
906                return anons[0];
907            }
908            const calls = [];
909            const constructs = [];
910            const stringIndices = [];
911            const numberIndices = [];
912            let stringIndexReadonly = false;
913            let numberIndexReadonly = false;
914            const props = createMultiMap<Type>();
915            for (const anon of anons) {
916                for (const p of checker.getPropertiesOfType(anon)) {
917                    props.add(p.name, p.valueDeclaration ? checker.getTypeOfSymbolAtLocation(p, p.valueDeclaration) : checker.getAnyType());
918                }
919                calls.push(...checker.getSignaturesOfType(anon, SignatureKind.Call));
920                constructs.push(...checker.getSignaturesOfType(anon, SignatureKind.Construct));
921                const stringIndexInfo = checker.getIndexInfoOfType(anon, IndexKind.String);
922                if (stringIndexInfo) {
923                    stringIndices.push(stringIndexInfo.type);
924                    stringIndexReadonly = stringIndexReadonly || stringIndexInfo.isReadonly;
925                }
926                const numberIndexInfo = checker.getIndexInfoOfType(anon, IndexKind.Number);
927                if (numberIndexInfo) {
928                    numberIndices.push(numberIndexInfo.type);
929                    numberIndexReadonly = numberIndexReadonly || numberIndexInfo.isReadonly;
930                }
931            }
932            const members = mapEntries(props, (name, types) => {
933                const isOptional = types.length < anons.length ? SymbolFlags.Optional : 0;
934                const s = checker.createSymbol(SymbolFlags.Property | isOptional, name as __String);
935                s.type = checker.getUnionType(types);
936                return [name, s];
937            });
938            const indexInfos = [];
939            if (stringIndices.length) indexInfos.push(checker.createIndexInfo(checker.getStringType(), checker.getUnionType(stringIndices), stringIndexReadonly));
940            if (numberIndices.length) indexInfos.push(checker.createIndexInfo(checker.getNumberType(), checker.getUnionType(numberIndices), numberIndexReadonly));
941            return checker.createAnonymousType(
942                anons[0].symbol,
943                members as UnderscoreEscapedMap<TransientSymbol>,
944                calls,
945                constructs,
946                indexInfos);
947        }
948
949        function inferTypes(usage: Usage): Type[] {
950            const types = [];
951
952            if (usage.isNumber) {
953                types.push(checker.getNumberType());
954            }
955            if (usage.isString) {
956                types.push(checker.getStringType());
957            }
958            if (usage.isNumberOrString) {
959                types.push(checker.getUnionType([checker.getStringType(), checker.getNumberType()]));
960            }
961            if (usage.numberIndex) {
962                types.push(checker.createArrayType(combineFromUsage(usage.numberIndex)));
963            }
964            if (usage.properties?.size || usage.constructs?.length || usage.stringIndex) {
965                types.push(inferStructuralType(usage));
966            }
967
968            const candidateTypes = (usage.candidateTypes || []).map(t => checker.getBaseTypeOfLiteralType(t));
969            const callsType = usage.calls?.length ? inferStructuralType(usage) : undefined;
970            if (callsType && candidateTypes) {
971                types.push(checker.getUnionType([callsType, ...candidateTypes], UnionReduction.Subtype));
972            }
973            else {
974                if (callsType) {
975                    types.push(callsType);
976                }
977                if (length(candidateTypes)) {
978                    types.push(...candidateTypes);
979                }
980            }
981
982            types.push(...inferNamedTypesFromProperties(usage));
983            return types;
984        }
985
986        function inferStructuralType(usage: Usage) {
987            const members = new Map<__String, Symbol>();
988            if (usage.properties) {
989                usage.properties.forEach((u, name) => {
990                    const symbol = checker.createSymbol(SymbolFlags.Property, name);
991                    symbol.type = combineFromUsage(u);
992                    members.set(name, symbol);
993                });
994            }
995            const callSignatures: Signature[] = usage.calls ? [getSignatureFromCalls(usage.calls)] : [];
996            const constructSignatures: Signature[] = usage.constructs ? [getSignatureFromCalls(usage.constructs)] : [];
997            const indexInfos = usage.stringIndex ? [checker.createIndexInfo(checker.getStringType(), combineFromUsage(usage.stringIndex), /*isReadonly*/ false)] : [];
998            return checker.createAnonymousType(/*symbol*/ undefined, members, callSignatures, constructSignatures, indexInfos);
999        }
1000
1001        function inferNamedTypesFromProperties(usage: Usage): Type[] {
1002            if (!usage.properties || !usage.properties.size) return [];
1003            const types = builtins.filter(t => allPropertiesAreAssignableToUsage(t, usage));
1004            if (0 < types.length && types.length < 3) {
1005                return types.map(t => inferInstantiationFromUsage(t, usage));
1006            }
1007            return [];
1008        }
1009
1010        function allPropertiesAreAssignableToUsage(type: Type, usage: Usage) {
1011            if (!usage.properties) return false;
1012            return !forEachEntry(usage.properties, (propUsage, name) => {
1013                const source = checker.getTypeOfPropertyOfType(type, name as string);
1014                if (!source) {
1015                    return true;
1016                }
1017                if (propUsage.calls) {
1018                    const sigs = checker.getSignaturesOfType(source, SignatureKind.Call);
1019                    return !sigs.length || !checker.isTypeAssignableTo(source, getFunctionFromCalls(propUsage.calls));
1020                }
1021                else {
1022                    return !checker.isTypeAssignableTo(source, combineFromUsage(propUsage));
1023                }
1024            });
1025        }
1026
1027        /**
1028         * inference is limited to
1029         * 1. generic types with a single parameter
1030         * 2. inference to/from calls with a single signature
1031         */
1032        function inferInstantiationFromUsage(type: Type, usage: Usage) {
1033            if (!(getObjectFlags(type) & ObjectFlags.Reference) || !usage.properties) {
1034                return type;
1035            }
1036            const generic = (type as TypeReference).target;
1037            const singleTypeParameter = singleOrUndefined(generic.typeParameters);
1038            if (!singleTypeParameter) return type;
1039
1040            const types: Type[] = [];
1041            usage.properties.forEach((propUsage, name) => {
1042                const genericPropertyType = checker.getTypeOfPropertyOfType(generic, name as string);
1043                Debug.assert(!!genericPropertyType, "generic should have all the properties of its reference.");
1044                types.push(...inferTypeParameters(genericPropertyType, combineFromUsage(propUsage), singleTypeParameter));
1045            });
1046            return builtinConstructors[type.symbol.escapedName as string](combineTypes(types));
1047        }
1048
1049        function inferTypeParameters(genericType: Type, usageType: Type, typeParameter: Type): readonly Type[] {
1050            if (genericType === typeParameter) {
1051                return [usageType];
1052            }
1053            else if (genericType.flags & TypeFlags.UnionOrIntersection) {
1054                return flatMap((genericType as UnionOrIntersectionType).types, t => inferTypeParameters(t, usageType, typeParameter));
1055            }
1056            else if (getObjectFlags(genericType) & ObjectFlags.Reference && getObjectFlags(usageType) & ObjectFlags.Reference) {
1057                // this is wrong because we need a reference to the targetType to, so we can check that it's also a reference
1058                const genericArgs = checker.getTypeArguments(genericType as TypeReference);
1059                const usageArgs = checker.getTypeArguments(usageType as TypeReference);
1060                const types = [];
1061                if (genericArgs && usageArgs) {
1062                    for (let i = 0; i < genericArgs.length; i++) {
1063                        if (usageArgs[i]) {
1064                            types.push(...inferTypeParameters(genericArgs[i], usageArgs[i], typeParameter));
1065                        }
1066                    }
1067                }
1068                return types;
1069            }
1070            const genericSigs = checker.getSignaturesOfType(genericType, SignatureKind.Call);
1071            const usageSigs = checker.getSignaturesOfType(usageType, SignatureKind.Call);
1072            if (genericSigs.length === 1 && usageSigs.length === 1) {
1073                return inferFromSignatures(genericSigs[0], usageSigs[0], typeParameter);
1074            }
1075            return [];
1076        }
1077
1078        function inferFromSignatures(genericSig: Signature, usageSig: Signature, typeParameter: Type) {
1079            const types = [];
1080            for (let i = 0; i < genericSig.parameters.length; i++) {
1081                const genericParam = genericSig.parameters[i];
1082                const usageParam = usageSig.parameters[i];
1083                const isRest = genericSig.declaration && isRestParameter(genericSig.declaration.parameters[i]);
1084                if (!usageParam) {
1085                    break;
1086                }
1087                let genericParamType = genericParam.valueDeclaration ? checker.getTypeOfSymbolAtLocation(genericParam, genericParam.valueDeclaration) : checker.getAnyType();
1088                const elementType = isRest && checker.getElementTypeOfArrayType(genericParamType);
1089                if (elementType) {
1090                    genericParamType = elementType;
1091                }
1092                const targetType = (usageParam as SymbolLinks).type
1093                    || (usageParam.valueDeclaration ? checker.getTypeOfSymbolAtLocation(usageParam, usageParam.valueDeclaration) : checker.getAnyType());
1094                types.push(...inferTypeParameters(genericParamType, targetType, typeParameter));
1095            }
1096            const genericReturn = checker.getReturnTypeOfSignature(genericSig);
1097            const usageReturn = checker.getReturnTypeOfSignature(usageSig);
1098            types.push(...inferTypeParameters(genericReturn, usageReturn, typeParameter));
1099            return types;
1100        }
1101
1102        function getFunctionFromCalls(calls: CallUsage[]) {
1103            return checker.createAnonymousType(/*symbol*/ undefined, createSymbolTable(), [getSignatureFromCalls(calls)], emptyArray, emptyArray);
1104        }
1105
1106        function getSignatureFromCalls(calls: CallUsage[]): Signature {
1107            const parameters: Symbol[] = [];
1108            const length = Math.max(...calls.map(c => c.argumentTypes.length));
1109            for (let i = 0; i < length; i++) {
1110                const symbol = checker.createSymbol(SymbolFlags.FunctionScopedVariable, escapeLeadingUnderscores(`arg${i}`));
1111                symbol.type = combineTypes(calls.map(call => call.argumentTypes[i] || checker.getUndefinedType()));
1112                if (calls.some(call => call.argumentTypes[i] === undefined)) {
1113                    symbol.flags |= SymbolFlags.Optional;
1114                }
1115                parameters.push(symbol);
1116            }
1117            const returnType = combineFromUsage(combineUsages(calls.map(call => call.return_)));
1118            return checker.createSignature(/*declaration*/ undefined, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, length, SignatureFlags.None);
1119        }
1120
1121        function addCandidateType(usage: Usage, type: Type | undefined) {
1122            if (type && !(type.flags & TypeFlags.Any) && !(type.flags & TypeFlags.Never)) {
1123                (usage.candidateTypes || (usage.candidateTypes = [])).push(type);
1124            }
1125        }
1126
1127        function addCandidateThisType(usage: Usage, type: Type | undefined) {
1128            if (type && !(type.flags & TypeFlags.Any) && !(type.flags & TypeFlags.Never)) {
1129                (usage.candidateThisTypes || (usage.candidateThisTypes = [])).push(type);
1130            }
1131        }
1132    }
1133}
1134