@@ -13297,6 +13297,97 @@ namespace ts {
13297
13297
getApparentTypeOfContextualType(node);
13298
13298
}
13299
13299
13300
+ function combineSignatures(signatureList: Signature[]): Signature {
13301
+ // Produce a synthetic signature whose arguments are a union of the parameters of the inferred signatures and whose return type is an intersection
13302
+ let parameters: Symbol[];
13303
+ let minimumParameterCount = Number.POSITIVE_INFINITY;
13304
+ let maximumRealParameterCount = 0;
13305
+ let restTypes: Type[];
13306
+ let thisTypes: Type[];
13307
+ let hasLiteralTypes = false;
13308
+
13309
+ // First, collect aggrgate statistics about all signatures to determine the characteristics of the resulting signature
13310
+ for (const signature of signatureList) {
13311
+ if (signature.minArgumentCount < minimumParameterCount) {
13312
+ minimumParameterCount = signature.minArgumentCount;
13313
+ }
13314
+ if (signature.hasRestParameter) {
13315
+ (restTypes || (restTypes = [])).push(getRestTypeOfSignature(signature));
13316
+ }
13317
+ if (signature.hasLiteralTypes) {
13318
+ hasLiteralTypes = true;
13319
+ }
13320
+ const realParameterCount = length(signature.parameters) - (signature.hasRestParameter ? 1 : 0);
13321
+ if (maximumRealParameterCount < realParameterCount) {
13322
+ maximumRealParameterCount = realParameterCount;
13323
+ }
13324
+ if (signature.thisParameter) {
13325
+ (thisTypes || (thisTypes = [])).push(getTypeOfSymbol(signature.thisParameter));
13326
+ }
13327
+ }
13328
+
13329
+ // Then, for every real parameter up to the maximum, combine those parameter types and names into a new symbol
13330
+ for (let i = 0; i < maximumRealParameterCount; i++) {
13331
+ const parameterNames: __String[] = [];
13332
+ const parameterTypes: Type[] = [];
13333
+ for (const signature of signatureList) {
13334
+ let type: Type;
13335
+ const index = signature.thisParameter ? i + 1 : i;
13336
+ if (index < (signature.hasRestParameter ? signature.parameters.length - 1 : signature.parameters.length)) {
13337
+ // If the argument is present, add it to the mixed argument
13338
+ const param = signature.parameters[index];
13339
+ if (!contains(parameterNames, param.escapedName)) {
13340
+ parameterNames.push(param.escapedName);
13341
+ }
13342
+ type = getTypeOfSymbol(param);
13343
+ }
13344
+ else if (signature.hasRestParameter) {
13345
+ // Otherwise, if there is a rest type for this signature, add that type
13346
+ type = getRestTypeOfSignature(signature);
13347
+ }
13348
+ else {
13349
+ // Otherwise, this argument may be `undefined` on some uses
13350
+ type = undefinedType;
13351
+ }
13352
+ if (!contains(parameterTypes, type)) {
13353
+ parameterTypes.push(type);
13354
+ }
13355
+ }
13356
+ // We do this so the name is reasonable for users
13357
+ const paramName = escapeLeadingUnderscores(map(parameterNames, unescapeLeadingUnderscores).join("or"));
13358
+ const paramSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, paramName);
13359
+ paramSymbol.type = getUnionType(parameterTypes);
13360
+ (parameters || (parameters = [])).push(paramSymbol);
13361
+ }
13362
+
13363
+ const hasRestParameter = !!(restTypes && restTypes.length);
13364
+ if (hasRestParameter) {
13365
+ const restSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "rest" as __String);
13366
+ restSymbol.type = getUnionType(restTypes);
13367
+ restSymbol.isRestParameter = true;
13368
+ (parameters || (parameters = [])).push(restSymbol);
13369
+ }
13370
+
13371
+ let thisParameterSymbol: TransientSymbol;
13372
+ if (thisTypes && thisTypes.length) {
13373
+ thisParameterSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "this" as __String);
13374
+ thisParameterSymbol.type = getUnionType(thisTypes);
13375
+ }
13376
+
13377
+ // TODO (weswigham): Merge type predicates?
13378
+ return createSignature(
13379
+ /*declaration*/ undefined,
13380
+ map(flatMap(signatureList, s => s.typeParameters), cloneTypeParameter),
13381
+ thisParameterSymbol,
13382
+ parameters,
13383
+ getIntersectionType(map(signatureList, getReturnTypeOfSignature)),
13384
+ /*typePredicate*/ undefined,
13385
+ minimumParameterCount,
13386
+ hasRestParameter,
13387
+ hasLiteralTypes
13388
+ );
13389
+ }
13390
+
13300
13391
// Return the contextual signature for a given expression node. A contextual type provides a
13301
13392
// contextual signature if it has a single call signature and if that call signature is non-generic.
13302
13393
// If the contextual type is a union type, get the signature from each type possible and if they are
@@ -13313,6 +13404,7 @@ namespace ts {
13313
13404
}
13314
13405
let signatureList: Signature[];
13315
13406
const types = (<UnionType>type).types;
13407
+ let mismatchedSignatures = false;
13316
13408
for (const current of types) {
13317
13409
const signature = getContextualCallSignature(current, node);
13318
13410
if (signature) {
@@ -13321,8 +13413,9 @@ namespace ts {
13321
13413
signatureList = [signature];
13322
13414
}
13323
13415
else if (!compareSignaturesIdentical(signatureList[0], signature, /*partialMatch*/ false, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true, compareTypesIdentical)) {
13324
- // Signatures aren't identical, do not use
13325
- return undefined;
13416
+ // Signatures aren't identical, set flag to union parameter types, intersect return types
13417
+ signatureList.push(signature);
13418
+ mismatchedSignatures = true;
13326
13419
}
13327
13420
else {
13328
13421
// Use this signature for contextual union signature
@@ -13331,6 +13424,10 @@ namespace ts {
13331
13424
}
13332
13425
}
13333
13426
13427
+ if (mismatchedSignatures) {
13428
+ return combineSignatures(signatureList);
13429
+ }
13430
+
13334
13431
// Result is union of signatures collected (return type is union of return types of this signature set)
13335
13432
let result: Signature;
13336
13433
if (signatureList) {
0 commit comments