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