@@ -13264,6 +13264,97 @@ namespace ts {
13264
13264
getApparentTypeOfContextualType(node);
13265
13265
}
13266
13266
13267
+ function combineSignatures(signatureList: Signature[]): Signature {
13268
+ // Produce a synthetic signature whose arguments are a union of the parameters of the inferred signatures and whose return type is an intersection
13269
+ let parameters: Symbol[];
13270
+ let minimumParameterCount = Number.POSITIVE_INFINITY;
13271
+ let maximumRealParameterCount = 0;
13272
+ let restTypes: Type[];
13273
+ let thisTypes: Type[];
13274
+ let hasLiteralTypes = false;
13275
+
13276
+ // First, collect aggrgate statistics about all signatures to determine the characteristics of the resulting signature
13277
+ for (const signature of signatureList) {
13278
+ if (signature.minArgumentCount < minimumParameterCount) {
13279
+ minimumParameterCount = signature.minArgumentCount;
13280
+ }
13281
+ if (signature.hasRestParameter) {
13282
+ (restTypes || (restTypes = [])).push(getRestTypeOfSignature(signature));
13283
+ }
13284
+ if (signature.hasLiteralTypes) {
13285
+ hasLiteralTypes = true;
13286
+ }
13287
+ const realParameterCount = length(signature.parameters) - (signature.hasRestParameter ? 1 : 0);
13288
+ if (maximumRealParameterCount < realParameterCount) {
13289
+ maximumRealParameterCount = realParameterCount;
13290
+ }
13291
+ if (signature.thisParameter) {
13292
+ (thisTypes || (thisTypes = [])).push(getTypeOfSymbol(signature.thisParameter));
13293
+ }
13294
+ }
13295
+
13296
+ // Then, for every real parameter up to the maximum, combine those parameter types and names into a new symbol
13297
+ for (let i = 0; i < maximumRealParameterCount; i++) {
13298
+ const parameterNames: __String[] = [];
13299
+ const parameterTypes: Type[] = [];
13300
+ for (const signature of signatureList) {
13301
+ let type: Type;
13302
+ const index = signature.thisParameter ? i + 1 : i;
13303
+ if (index < (signature.hasRestParameter ? signature.parameters.length - 1 : signature.parameters.length)) {
13304
+ // If the argument is present, add it to the mixed argument
13305
+ const param = signature.parameters[index];
13306
+ if (!contains(parameterNames, param.escapedName)) {
13307
+ parameterNames.push(param.escapedName);
13308
+ }
13309
+ type = getTypeOfSymbol(param);
13310
+ }
13311
+ else if (signature.hasRestParameter) {
13312
+ // Otherwise, if there is a rest type for this signature, add that type
13313
+ type = getRestTypeOfSignature(signature);
13314
+ }
13315
+ else {
13316
+ // Otherwise, this argument may be `undefined` on some uses
13317
+ type = undefinedType;
13318
+ }
13319
+ if (!contains(parameterTypes, type)) {
13320
+ parameterTypes.push(type);
13321
+ }
13322
+ }
13323
+ // We do this so the name is reasonable for users
13324
+ const paramName = escapeLeadingUnderscores(map(parameterNames, unescapeLeadingUnderscores).join("or"));
13325
+ const paramSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, paramName);
13326
+ paramSymbol.type = getUnionType(parameterTypes);
13327
+ (parameters || (parameters = [])).push(paramSymbol);
13328
+ }
13329
+
13330
+ const hasRestParameter = !!(restTypes && restTypes.length);
13331
+ if (hasRestParameter) {
13332
+ const restSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "rest" as __String);
13333
+ restSymbol.type = getUnionType(restTypes);
13334
+ restSymbol.isRestParameter = true;
13335
+ (parameters || (parameters = [])).push(restSymbol);
13336
+ }
13337
+
13338
+ let thisParameterSymbol: TransientSymbol;
13339
+ if (thisTypes && thisTypes.length) {
13340
+ thisParameterSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "this" as __String);
13341
+ thisParameterSymbol.type = getUnionType(thisTypes);
13342
+ }
13343
+
13344
+ // TODO (weswigham): Merge type predicates?
13345
+ return createSignature(
13346
+ /*declaration*/ undefined,
13347
+ map(flatMap(signatureList, s => s.typeParameters), cloneTypeParameter),
13348
+ thisParameterSymbol,
13349
+ parameters,
13350
+ getIntersectionType(map(signatureList, getReturnTypeOfSignature)),
13351
+ /*typePredicate*/ undefined,
13352
+ minimumParameterCount,
13353
+ hasRestParameter,
13354
+ hasLiteralTypes
13355
+ );
13356
+ }
13357
+
13267
13358
// Return the contextual signature for a given expression node. A contextual type provides a
13268
13359
// contextual signature if it has a single call signature and if that call signature is non-generic.
13269
13360
// If the contextual type is a union type, get the signature from each type possible and if they are
@@ -13280,6 +13371,7 @@ namespace ts {
13280
13371
}
13281
13372
let signatureList: Signature[];
13282
13373
const types = (<UnionType>type).types;
13374
+ let mismatchedSignatures = false;
13283
13375
for (const current of types) {
13284
13376
const signature = getContextualCallSignature(current, node);
13285
13377
if (signature) {
@@ -13288,8 +13380,9 @@ namespace ts {
13288
13380
signatureList = [signature];
13289
13381
}
13290
13382
else if (!compareSignaturesIdentical(signatureList[0], signature, /*partialMatch*/ false, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true, compareTypesIdentical)) {
13291
- // Signatures aren't identical, do not use
13292
- return undefined;
13383
+ // Signatures aren't identical, set flag to union parameter types, intersect return types
13384
+ signatureList.push(signature);
13385
+ mismatchedSignatures = true;
13293
13386
}
13294
13387
else {
13295
13388
// Use this signature for contextual union signature
@@ -13298,6 +13391,10 @@ namespace ts {
13298
13391
}
13299
13392
}
13300
13393
13394
+ if (mismatchedSignatures) {
13395
+ return combineSignatures(signatureList);
13396
+ }
13397
+
13301
13398
// Result is union of signatures collected (return type is union of return types of this signature set)
13302
13399
let result: Signature;
13303
13400
if (signatureList) {
0 commit comments