@@ -13297,6 +13297,97 @@ namespace ts {
1329713297 getApparentTypeOfContextualType(node);
1329813298 }
1329913299
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+
1330013391 // Return the contextual signature for a given expression node. A contextual type provides a
1330113392 // contextual signature if it has a single call signature and if that call signature is non-generic.
1330213393 // 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 {
1331313404 }
1331413405 let signatureList: Signature[];
1331513406 const types = (<UnionType>type).types;
13407+ let mismatchedSignatures = false;
1331613408 for (const current of types) {
1331713409 const signature = getContextualCallSignature(current, node);
1331813410 if (signature) {
@@ -13321,8 +13413,9 @@ namespace ts {
1332113413 signatureList = [signature];
1332213414 }
1332313415 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;
1332613419 }
1332713420 else {
1332813421 // Use this signature for contextual union signature
@@ -13331,6 +13424,10 @@ namespace ts {
1333113424 }
1333213425 }
1333313426
13427+ if (mismatchedSignatures) {
13428+ return combineSignatures(signatureList);
13429+ }
13430+
1333413431 // Result is union of signatures collected (return type is union of return types of this signature set)
1333513432 let result: Signature;
1333613433 if (signatureList) {
0 commit comments