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