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