@@ -13372,6 +13372,97 @@ namespace ts {
13372
13372
getApparentTypeOfContextualType(node);
13373
13373
}
13374
13374
13375
+ function combineSignatures(signatureList: Signature[]): Signature {
13376
+ // Produce a synthetic signature whose arguments are a union of the parameters of the inferred signatures and whose return type is an intersection
13377
+ let parameters: Symbol[];
13378
+ let minimumParameterCount = Number.POSITIVE_INFINITY;
13379
+ let maximumRealParameterCount = 0;
13380
+ let restTypes: Type[];
13381
+ let thisTypes: Type[];
13382
+ let hasLiteralTypes = false;
13383
+
13384
+ // First, collect aggrgate statistics about all signatures to determine the characteristics of the resulting signature
13385
+ for (const signature of signatureList) {
13386
+ if (signature.minArgumentCount < minimumParameterCount) {
13387
+ minimumParameterCount = signature.minArgumentCount;
13388
+ }
13389
+ if (signature.hasRestParameter) {
13390
+ (restTypes || (restTypes = [])).push(getRestTypeOfSignature(signature));
13391
+ }
13392
+ if (signature.hasLiteralTypes) {
13393
+ hasLiteralTypes = true;
13394
+ }
13395
+ const realParameterCount = length(signature.parameters) - (signature.hasRestParameter ? 1 : 0);
13396
+ if (maximumRealParameterCount < realParameterCount) {
13397
+ maximumRealParameterCount = realParameterCount;
13398
+ }
13399
+ if (signature.thisParameter) {
13400
+ (thisTypes || (thisTypes = [])).push(getTypeOfSymbol(signature.thisParameter));
13401
+ }
13402
+ }
13403
+
13404
+ // Then, for every real parameter up to the maximum, combine those parameter types and names into a new symbol
13405
+ for (let i = 0; i < maximumRealParameterCount; i++) {
13406
+ const parameterNames: __String[] = [];
13407
+ const parameterTypes: Type[] = [];
13408
+ for (const signature of signatureList) {
13409
+ let type: Type;
13410
+ const index = signature.thisParameter ? i + 1 : i;
13411
+ if (index < (signature.hasRestParameter ? signature.parameters.length - 1 : signature.parameters.length)) {
13412
+ // If the argument is present, add it to the mixed argument
13413
+ const param = signature.parameters[index];
13414
+ if (!contains(parameterNames, param.escapedName)) {
13415
+ parameterNames.push(param.escapedName);
13416
+ }
13417
+ type = getTypeOfSymbol(param);
13418
+ }
13419
+ else if (signature.hasRestParameter) {
13420
+ // Otherwise, if there is a rest type for this signature, add that type
13421
+ type = getRestTypeOfSignature(signature);
13422
+ }
13423
+ else {
13424
+ // Otherwise, this argument may be `undefined` on some uses
13425
+ type = undefinedType;
13426
+ }
13427
+ if (!contains(parameterTypes, type)) {
13428
+ parameterTypes.push(type);
13429
+ }
13430
+ }
13431
+ // We do this so the name is reasonable for users
13432
+ const paramName = escapeLeadingUnderscores(map(parameterNames, unescapeLeadingUnderscores).join("or"));
13433
+ const paramSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, paramName);
13434
+ paramSymbol.type = getUnionType(parameterTypes);
13435
+ (parameters || (parameters = [])).push(paramSymbol);
13436
+ }
13437
+
13438
+ const hasRestParameter = !!(restTypes && restTypes.length);
13439
+ if (hasRestParameter) {
13440
+ const restSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "rest" as __String);
13441
+ restSymbol.type = getUnionType(restTypes);
13442
+ restSymbol.isRestParameter = true;
13443
+ (parameters || (parameters = [])).push(restSymbol);
13444
+ }
13445
+
13446
+ let thisParameterSymbol: TransientSymbol;
13447
+ if (thisTypes && thisTypes.length) {
13448
+ thisParameterSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "this" as __String);
13449
+ thisParameterSymbol.type = getUnionType(thisTypes);
13450
+ }
13451
+
13452
+ // TODO (weswigham): Merge type predicates?
13453
+ return createSignature(
13454
+ /*declaration*/ undefined,
13455
+ map(flatMap(signatureList, s => s.typeParameters), cloneTypeParameter),
13456
+ thisParameterSymbol,
13457
+ parameters,
13458
+ getIntersectionType(map(signatureList, getReturnTypeOfSignature)),
13459
+ /*typePredicate*/ undefined,
13460
+ minimumParameterCount,
13461
+ hasRestParameter,
13462
+ hasLiteralTypes
13463
+ );
13464
+ }
13465
+
13375
13466
// Return the contextual signature for a given expression node. A contextual type provides a
13376
13467
// contextual signature if it has a single call signature and if that call signature is non-generic.
13377
13468
// If the contextual type is a union type, get the signature from each type possible and if they are
@@ -13388,6 +13479,7 @@ namespace ts {
13388
13479
}
13389
13480
let signatureList: Signature[];
13390
13481
const types = (<UnionType>type).types;
13482
+ let mismatchedSignatures = false;
13391
13483
for (const current of types) {
13392
13484
const signature = getContextualCallSignature(current, node);
13393
13485
if (signature) {
@@ -13396,8 +13488,9 @@ namespace ts {
13396
13488
signatureList = [signature];
13397
13489
}
13398
13490
else if (!compareSignaturesIdentical(signatureList[0], signature, /*partialMatch*/ false, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true, compareTypesIdentical)) {
13399
- // Signatures aren't identical, do not use
13400
- return undefined;
13491
+ // Signatures aren't identical, set flag to union parameter types, intersect return types
13492
+ signatureList.push(signature);
13493
+ mismatchedSignatures = true;
13401
13494
}
13402
13495
else {
13403
13496
// Use this signature for contextual union signature
@@ -13406,6 +13499,10 @@ namespace ts {
13406
13499
}
13407
13500
}
13408
13501
13502
+ if (mismatchedSignatures) {
13503
+ return combineSignatures(signatureList);
13504
+ }
13505
+
13409
13506
// Result is union of signatures collected (return type is union of return types of this signature set)
13410
13507
let result: Signature;
13411
13508
if (signatureList) {
0 commit comments