@@ -6799,7 +6799,12 @@ namespace ts {
6799
6799
// type is the union of the constituent return types.
6800
6800
function getUnionSignatures(signatureLists: ReadonlyArray<ReadonlyArray<Signature>>): Signature[] {
6801
6801
let result: Signature[] | undefined;
6802
+ let indexWithLengthOverOne: number | undefined;
6802
6803
for (let i = 0; i < signatureLists.length; i++) {
6804
+ if (signatureLists[i].length === 0) return emptyArray;
6805
+ if (signatureLists[i].length > 1) {
6806
+ indexWithLengthOverOne = indexWithLengthOverOne === undefined ? i : -1; // -1 is a signal there are multiple overload sets
6807
+ }
6803
6808
for (const signature of signatureLists[i]) {
6804
6809
// Only process signatures with parameter lists that aren't already in the result list
6805
6810
if (!result || !findMatchingSignature(result, signature, /*partialMatch*/ false, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true)) {
@@ -6823,9 +6828,91 @@ namespace ts {
6823
6828
}
6824
6829
}
6825
6830
}
6831
+ if (!length(result) && indexWithLengthOverOne !== -1) {
6832
+ // No sufficiently similar signature existed to subsume all the other signatures in the union - time to see if we can make a single
6833
+ // signature that handles all over them. We only do this when there are overloads in only one constituent.
6834
+ // (Overloads are conditional in nature and having overloads in multiple constituents would necessitate making a power set of
6835
+ // signatures from the type, whose ordering would be non-obvious)
6836
+ const masterList = signatureLists[indexWithLengthOverOne !== undefined ? indexWithLengthOverOne : 0];
6837
+ let results: Signature[] | undefined = masterList.slice();
6838
+ for (const signatures of signatureLists) {
6839
+ if (signatures !== masterList) {
6840
+ const signature = signatures[0];
6841
+ Debug.assert(!!signature, "getUnionSignatures bails early on empty signature lists and should not have empty lists on second pass");
6842
+ results = signature.typeParameters && some(results, s => !!s.typeParameters) ? undefined : map(results, sig => combineSignaturesOfUnionMembers(sig, signature));
6843
+ if (!results) {
6844
+ break;
6845
+ }
6846
+ }
6847
+ }
6848
+ result = results;
6849
+ }
6826
6850
return result || emptyArray;
6827
6851
}
6828
6852
6853
+ function combineUnionThisParam(left: Symbol | undefined, right: Symbol | undefined): Symbol | undefined {
6854
+ if (!left || !right) {
6855
+ return left || right;
6856
+ }
6857
+ // A signature `this` type might be a read or a write position... It's very possible that it should be invariant
6858
+ // and we should refuse to merge signatures if there are `this` types and they do not match. However, so as to be
6859
+ // permissive when calling, for now, we'll union the `this` types just like the overlapping-union-signature check does
6860
+ const thisType = getUnionType([getTypeOfSymbol(left), getTypeOfSymbol(right)], UnionReduction.Subtype);
6861
+ return createSymbolWithType(left, thisType);
6862
+ }
6863
+
6864
+ function combineUnionParameters(left: Signature, right: Signature) {
6865
+ const longest = getParameterCount(left) >= getParameterCount(right) ? left : right;
6866
+ const shorter = longest === left ? right : left;
6867
+ const longestCount = getParameterCount(longest);
6868
+ const eitherHasEffectiveRest = (hasEffectiveRestParameter(left) || hasEffectiveRestParameter(right));
6869
+ const needsExtraRestElement = eitherHasEffectiveRest && !hasEffectiveRestParameter(longest);
6870
+ const params = new Array<Symbol>(longestCount + (needsExtraRestElement ? 1 : 0));
6871
+ for (let i = 0; i < longestCount; i++) {
6872
+ const longestParamType = tryGetTypeAtPosition(longest, i)!;
6873
+ const shorterParamType = tryGetTypeAtPosition(shorter, i) || unknownType;
6874
+ const unionParamType = getIntersectionType([longestParamType, shorterParamType]);
6875
+ const isRestParam = eitherHasEffectiveRest && !needsExtraRestElement && i === (longestCount - 1);
6876
+ const isOptional = i >= getMinArgumentCount(longest) && i >= getMinArgumentCount(shorter);
6877
+ const leftName = getParameterNameAtPosition(left, i);
6878
+ const rightName = getParameterNameAtPosition(right, i);
6879
+ const paramSymbol = createSymbol(
6880
+ SymbolFlags.FunctionScopedVariable | (isOptional && !isRestParam ? SymbolFlags.Optional : 0),
6881
+ leftName === rightName ? leftName : `arg${i}` as __String
6882
+ );
6883
+ paramSymbol.type = isRestParam ? createArrayType(unionParamType) : unionParamType;
6884
+ params[i] = paramSymbol;
6885
+ }
6886
+ if (needsExtraRestElement) {
6887
+ const restParamSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "args" as __String);
6888
+ restParamSymbol.type = createArrayType(getTypeAtPosition(shorter, longestCount));
6889
+ params[longestCount] = restParamSymbol;
6890
+ }
6891
+ return params;
6892
+ }
6893
+
6894
+ function combineSignaturesOfUnionMembers(left: Signature, right: Signature): Signature {
6895
+ const declaration = left.declaration;
6896
+ const params = combineUnionParameters(left, right);
6897
+ const thisParam = combineUnionThisParam(left.thisParameter, right.thisParameter);
6898
+ const minArgCount = Math.max(left.minArgumentCount, right.minArgumentCount);
6899
+ const hasRestParam = left.hasRestParameter || right.hasRestParameter;
6900
+ const hasLiteralTypes = left.hasLiteralTypes || right.hasLiteralTypes;
6901
+ const result = createSignature(
6902
+ declaration,
6903
+ left.typeParameters || right.typeParameters,
6904
+ thisParam,
6905
+ params,
6906
+ /*resolvedReturnType*/ undefined,
6907
+ /*resolvedTypePredicate*/ undefined,
6908
+ minArgCount,
6909
+ hasRestParam,
6910
+ hasLiteralTypes
6911
+ );
6912
+ result.unionSignatures = concatenate(left.unionSignatures || [left], [right]);
6913
+ return result;
6914
+ }
6915
+
6829
6916
function getUnionIndexInfo(types: ReadonlyArray<Type>, kind: IndexKind): IndexInfo | undefined {
6830
6917
const indexTypes: Type[] = [];
6831
6918
let isAnyReadonly = false;
@@ -17566,6 +17653,26 @@ namespace ts {
17566
17653
}
17567
17654
17568
17655
function getJsxPropsTypeForSignatureFromMember(sig: Signature, forcedLookupLocation: __String) {
17656
+ if (sig.unionSignatures) {
17657
+ // JSX Elements using the legacy `props`-field based lookup (eg, react class components) need to treat the `props` member as an input
17658
+ // instead of an output position when resolving the signature. We need to go back to the input signatures of the composite signature,
17659
+ // get the type of `props` on each return type individually, and then _intersect them_, rather than union them (as would normally occur
17660
+ // for a union signature). It's an unfortunate quirk of looking in the output of the signature for the type we want to use for the input.
17661
+ // The default behavior of `getTypeOfFirstParameterOfSignatureWithFallback` when no `props` member name is defined is much more sane.
17662
+ const results: Type[] = [];
17663
+ for (const signature of sig.unionSignatures) {
17664
+ const instance = getReturnTypeOfSignature(signature);
17665
+ if (isTypeAny(instance)) {
17666
+ return instance;
17667
+ }
17668
+ const propType = getTypeOfPropertyOfType(instance, forcedLookupLocation);
17669
+ if (!propType) {
17670
+ return;
17671
+ }
17672
+ results.push(propType);
17673
+ }
17674
+ return getIntersectionType(results);
17675
+ }
17569
17676
const instanceType = getReturnTypeOfSignature(sig);
17570
17677
return isTypeAny(instanceType) ? instanceType : getTypeOfPropertyOfType(instanceType, forcedLookupLocation);
17571
17678
}
0 commit comments