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