@@ -13392,6 +13392,97 @@ namespace ts {
1339213392 getApparentTypeOfContextualType(node);
1339313393 }
1339413394
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+
1339513486 // Return the contextual signature for a given expression node. A contextual type provides a
1339613487 // contextual signature if it has a single call signature and if that call signature is non-generic.
1339713488 // 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 {
1340813499 }
1340913500 let signatureList: Signature[];
1341013501 const types = (<UnionType>type).types;
13502+ let mismatchedSignatures = false;
1341113503 for (const current of types) {
1341213504 const signature = getContextualCallSignature(current, node);
1341313505 if (signature) {
@@ -13416,8 +13508,9 @@ namespace ts {
1341613508 signatureList = [signature];
1341713509 }
1341813510 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;
1342113514 }
1342213515 else {
1342313516 // Use this signature for contextual union signature
@@ -13426,6 +13519,10 @@ namespace ts {
1342613519 }
1342713520 }
1342813521
13522+ if (mismatchedSignatures) {
13523+ return combineSignatures(signatureList);
13524+ }
13525+
1342913526 // Result is union of signatures collected (return type is union of return types of this signature set)
1343013527 let result: Signature;
1343113528 if (signatureList) {
0 commit comments