@@ -10274,7 +10274,7 @@ namespace ts {
10274
10274
if (signatures !== masterList) {
10275
10275
const signature = signatures[0];
10276
10276
Debug.assert(!!signature, "getUnionSignatures bails early on empty signature lists and should not have empty lists on second pass");
10277
- results = signature.typeParameters && some(results, s => !!s.typeParameters) ? undefined : map(results, sig => combineSignaturesOfUnionMembers(sig, signature));
10277
+ results = signature.typeParameters && some(results, s => !!s.typeParameters && !compareTypeParametersIdentical(signature.typeParameters!, s.typeParameters) ) ? undefined : map(results, sig => combineSignaturesOfUnionMembers(sig, signature));
10278
10278
if (!results) {
10279
10279
break;
10280
10280
}
@@ -10285,18 +10285,39 @@ namespace ts {
10285
10285
return result || emptyArray;
10286
10286
}
10287
10287
10288
- function combineUnionThisParam(left: Symbol | undefined, right: Symbol | undefined): Symbol | undefined {
10288
+ function compareTypeParametersIdentical(sourceParams: readonly TypeParameter[], targetParams: readonly TypeParameter[]): boolean {
10289
+ if (sourceParams.length !== targetParams.length) {
10290
+ return false;
10291
+ }
10292
+
10293
+ const mapper = createTypeMapper(targetParams, sourceParams);
10294
+ for (let i = 0; i < sourceParams.length; i++) {
10295
+ const source = sourceParams[i];
10296
+ const target = targetParams[i];
10297
+ if (source === target) continue;
10298
+ // We instantiate the target type parameter constraints into the source types so we can recognize `<T, U extends T>` as the same as `<A, B extends A>`
10299
+ if (!isTypeIdenticalTo(getConstraintFromTypeParameter(source) || unknownType, instantiateType(getConstraintFromTypeParameter(target) || unknownType, mapper))) return false;
10300
+ // We don't compare defaults - we just use the type parameter defaults from the first signature that seems to match.
10301
+ // It might make sense to combine these defaults in the future, but doing so intelligently requires knowing
10302
+ // if the parameter is used covariantly or contravariantly (so we intersect if it's used like a parameter or union if used like a return type)
10303
+ // and, since it's just an inference _default_, just picking one arbitrarily works OK.
10304
+ }
10305
+
10306
+ return true;
10307
+ }
10308
+
10309
+ function combineUnionThisParam(left: Symbol | undefined, right: Symbol | undefined, mapper: TypeMapper | undefined): Symbol | undefined {
10289
10310
if (!left || !right) {
10290
10311
return left || right;
10291
10312
}
10292
10313
// A signature `this` type might be a read or a write position... It's very possible that it should be invariant
10293
10314
// and we should refuse to merge signatures if there are `this` types and they do not match. However, so as to be
10294
10315
// permissive when calling, for now, we'll intersect the `this` types just like we do for param types in union signatures.
10295
- const thisType = getIntersectionType([getTypeOfSymbol(left), getTypeOfSymbol(right)]);
10316
+ const thisType = getIntersectionType([getTypeOfSymbol(left), instantiateType( getTypeOfSymbol(right), mapper )]);
10296
10317
return createSymbolWithType(left, thisType);
10297
10318
}
10298
10319
10299
- function combineUnionParameters(left: Signature, right: Signature) {
10320
+ function combineUnionParameters(left: Signature, right: Signature, mapper: TypeMapper | undefined ) {
10300
10321
const leftCount = getParameterCount(left);
10301
10322
const rightCount = getParameterCount(right);
10302
10323
const longest = leftCount >= rightCount ? left : right;
@@ -10306,8 +10327,14 @@ namespace ts {
10306
10327
const needsExtraRestElement = eitherHasEffectiveRest && !hasEffectiveRestParameter(longest);
10307
10328
const params = new Array<Symbol>(longestCount + (needsExtraRestElement ? 1 : 0));
10308
10329
for (let i = 0; i < longestCount; i++) {
10309
- const longestParamType = tryGetTypeAtPosition(longest, i)!;
10310
- const shorterParamType = tryGetTypeAtPosition(shorter, i) || unknownType;
10330
+ let longestParamType = tryGetTypeAtPosition(longest, i)!;
10331
+ if (longest === right) {
10332
+ longestParamType = instantiateType(longestParamType, mapper);
10333
+ }
10334
+ let shorterParamType = tryGetTypeAtPosition(shorter, i) || unknownType;
10335
+ if (shorter === right) {
10336
+ shorterParamType = instantiateType(shorterParamType, mapper);
10337
+ }
10311
10338
const unionParamType = getIntersectionType([longestParamType, shorterParamType]);
10312
10339
const isRestParam = eitherHasEffectiveRest && !needsExtraRestElement && i === (longestCount - 1);
10313
10340
const isOptional = i >= getMinArgumentCount(longest) && i >= getMinArgumentCount(shorter);
@@ -10328,19 +10355,28 @@ namespace ts {
10328
10355
if (needsExtraRestElement) {
10329
10356
const restParamSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "args" as __String);
10330
10357
restParamSymbol.type = createArrayType(getTypeAtPosition(shorter, longestCount));
10358
+ if (shorter === right) {
10359
+ restParamSymbol.type = instantiateType(restParamSymbol.type, mapper);
10360
+ }
10331
10361
params[longestCount] = restParamSymbol;
10332
10362
}
10333
10363
return params;
10334
10364
}
10335
10365
10336
10366
function combineSignaturesOfUnionMembers(left: Signature, right: Signature): Signature {
10367
+ const typeParams = left.typeParameters || right.typeParameters;
10368
+ let paramMapper: TypeMapper | undefined;
10369
+ if (left.typeParameters && right.typeParameters) {
10370
+ paramMapper = createTypeMapper(right.typeParameters, left.typeParameters);
10371
+ // We just use the type parameter defaults from the first signature
10372
+ }
10337
10373
const declaration = left.declaration;
10338
- const params = combineUnionParameters(left, right);
10339
- const thisParam = combineUnionThisParam(left.thisParameter, right.thisParameter);
10374
+ const params = combineUnionParameters(left, right, paramMapper );
10375
+ const thisParam = combineUnionThisParam(left.thisParameter, right.thisParameter, paramMapper );
10340
10376
const minArgCount = Math.max(left.minArgumentCount, right.minArgumentCount);
10341
10377
const result = createSignature(
10342
10378
declaration,
10343
- left.typeParameters || right.typeParameters ,
10379
+ typeParams ,
10344
10380
thisParam,
10345
10381
params,
10346
10382
/*resolvedReturnType*/ undefined,
@@ -10349,6 +10385,9 @@ namespace ts {
10349
10385
(left.flags | right.flags) & SignatureFlags.PropagatingFlags
10350
10386
);
10351
10387
result.unionSignatures = concatenate(left.unionSignatures || [left], [right]);
10388
+ if (paramMapper) {
10389
+ result.mapper = left.mapper && left.unionSignatures ? combineTypeMappers(left.mapper, paramMapper) : paramMapper;
10390
+ }
10352
10391
return result;
10353
10392
}
10354
10393
@@ -11882,7 +11921,7 @@ namespace ts {
11882
11921
return errorType;
11883
11922
}
11884
11923
let type = signature.target ? instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper) :
11885
- signature.unionSignatures ? getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype) :
11924
+ signature.unionSignatures ? instantiateType( getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype), signature.mapper ) :
11886
11925
getReturnTypeFromAnnotation(signature.declaration!) ||
11887
11926
(nodeIsMissing((<FunctionLikeDeclaration>signature.declaration).body) ? anyType : getReturnTypeFromBody(<FunctionLikeDeclaration>signature.declaration));
11888
11927
if (signature.flags & SignatureFlags.IsInnerCallChain) {
0 commit comments