Skip to content

Commit c6fde09

Browse files
committed
Amalgamate intersection composite signature return types as intersections, rather than the prior exclusively union behavior
1 parent 0b7a99c commit c6fde09

File tree

2 files changed

+35
-19
lines changed

2 files changed

+35
-19
lines changed

src/compiler/checker.ts

+32-18
Original file line numberDiff line numberDiff line change
@@ -10206,7 +10206,8 @@ namespace ts {
1020610206
sig.resolvedMinArgumentCount = undefined;
1020710207
sig.target = undefined;
1020810208
sig.mapper = undefined;
10209-
sig.unionSignatures = undefined;
10209+
sig.compositeSignatures = undefined;
10210+
sig.compositeKind = undefined;
1021010211
return sig;
1021110212
}
1021210213

@@ -10215,13 +10216,15 @@ namespace ts {
1021510216
/*resolvedTypePredicate*/ undefined, sig.minArgumentCount, sig.flags & SignatureFlags.PropagatingFlags);
1021610217
result.target = sig.target;
1021710218
result.mapper = sig.mapper;
10218-
result.unionSignatures = sig.unionSignatures;
10219+
result.compositeSignatures = sig.compositeSignatures;
10220+
result.compositeKind = sig.compositeKind;
1021910221
return result;
1022010222
}
1022110223

1022210224
function createUnionSignature(signature: Signature, unionSignatures: Signature[]) {
1022310225
const result = cloneSignature(signature);
10224-
result.unionSignatures = unionSignatures;
10226+
result.compositeSignatures = unionSignatures;
10227+
result.compositeKind = TypeFlags.Union;
1022510228
result.target = undefined;
1022610229
result.mapper = undefined;
1022710230
return result;
@@ -10495,9 +10498,10 @@ namespace ts {
1049510498
minArgCount,
1049610499
(left.flags | right.flags) & SignatureFlags.PropagatingFlags
1049710500
);
10498-
result.unionSignatures = concatenate(left.unionSignatures || [left], [right]);
10501+
result.compositeKind = TypeFlags.Union;
10502+
result.compositeSignatures = concatenate(left.compositeKind !== TypeFlags.Intersection && left.compositeSignatures || [left], [right]);
1049910503
if (paramMapper) {
10500-
result.mapper = left.mapper && left.unionSignatures ? combineTypeMappers(left.mapper, paramMapper) : paramMapper;
10504+
result.mapper = left.compositeKind !== TypeFlags.Intersection && left.mapper && left.compositeSignatures ? combineTypeMappers(left.mapper, paramMapper) : paramMapper;
1050110505
}
1050210506
return result;
1050310507
}
@@ -12015,8 +12019,8 @@ namespace ts {
1201512019
const targetTypePredicate = getTypePredicateOfSignature(signature.target);
1201612020
signature.resolvedTypePredicate = targetTypePredicate ? instantiateTypePredicate(targetTypePredicate, signature.mapper!) : noTypePredicate;
1201712021
}
12018-
else if (signature.unionSignatures) {
12019-
signature.resolvedTypePredicate = getUnionTypePredicate(signature.unionSignatures) || noTypePredicate;
12022+
else if (signature.compositeSignatures) {
12023+
signature.resolvedTypePredicate = getUnionOrIntersectionTypePredicate(signature.compositeSignatures, signature.compositeKind) || noTypePredicate;
1202012024
}
1202112025
else {
1202212026
const type = signature.declaration && getEffectiveReturnTypeNode(signature.declaration);
@@ -12045,13 +12049,17 @@ namespace ts {
1204512049
findIndex(signature.parameters, p => p.escapedName === parameterName.escapedText), type);
1204612050
}
1204712051

12052+
function getUnionOrIntersectionType(types: Type[], kind: TypeFlags | undefined, unionReduction?: UnionReduction) {
12053+
return kind !== TypeFlags.Intersection ? getUnionType(types, unionReduction) : getIntersectionType(types);
12054+
}
12055+
1204812056
function getReturnTypeOfSignature(signature: Signature): Type {
1204912057
if (!signature.resolvedReturnType) {
1205012058
if (!pushTypeResolution(signature, TypeSystemPropertyName.ResolvedReturnType)) {
1205112059
return errorType;
1205212060
}
1205312061
let type = signature.target ? instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper) :
12054-
signature.unionSignatures ? instantiateType(getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype), signature.mapper) :
12062+
signature.compositeSignatures ? instantiateType(getUnionOrIntersectionType(map(signature.compositeSignatures, getReturnTypeOfSignature), signature.compositeKind, UnionReduction.Subtype), signature.mapper) :
1205512063
getReturnTypeFromAnnotation(signature.declaration!) ||
1205612064
(nodeIsMissing((<FunctionLikeDeclaration>signature.declaration).body) ? anyType : getReturnTypeFromBody(<FunctionLikeDeclaration>signature.declaration));
1205712065
if (signature.flags & SignatureFlags.IsInnerCallChain) {
@@ -13481,13 +13489,18 @@ namespace ts {
1348113489
return getUnionTypeFromSortedList(typeSet, objectFlags, aliasSymbol, aliasTypeArguments, origin);
1348213490
}
1348313491

13484-
function getUnionTypePredicate(signatures: readonly Signature[]): TypePredicate | undefined {
13492+
function getUnionOrIntersectionTypePredicate(signatures: readonly Signature[], kind: TypeFlags | undefined): TypePredicate | undefined {
1348513493
let first: TypePredicate | undefined;
1348613494
const types: Type[] = [];
1348713495
for (const sig of signatures) {
1348813496
const pred = getTypePredicateOfSignature(sig);
1348913497
if (!pred || pred.kind === TypePredicateKind.AssertsThis || pred.kind === TypePredicateKind.AssertsIdentifier) {
13490-
continue;
13498+
if (kind !== TypeFlags.Intersection) {
13499+
continue;
13500+
}
13501+
else {
13502+
return; // intersections demand all members be type predicates for the result to have a predicate
13503+
}
1349113504
}
1349213505

1349313506
if (first) {
@@ -13502,11 +13515,11 @@ namespace ts {
1350213515
types.push(pred.type);
1350313516
}
1350413517
if (!first) {
13505-
// No union signatures had a type predicate.
13518+
// No signatures had a type predicate.
1350613519
return undefined;
1350713520
}
13508-
const unionType = getUnionType(types);
13509-
return createTypePredicate(first.kind, first.parameterName, first.parameterIndex, unionType);
13521+
const compositeType = kind !== TypeFlags.Intersection ? getUnionType(types) : getIntersectionType(types);
13522+
return createTypePredicate(first.kind, first.parameterName, first.parameterIndex, compositeType);
1351013523
}
1351113524

1351213525
function typePredicateKindsMatch(a: TypePredicate, b: TypePredicate): boolean {
@@ -24812,14 +24825,14 @@ namespace ts {
2481224825
}
2481324826

2481424827
function getJsxPropsTypeForSignatureFromMember(sig: Signature, forcedLookupLocation: __String) {
24815-
if (sig.unionSignatures) {
24828+
if (sig.compositeSignatures) {
2481624829
// JSX Elements using the legacy `props`-field based lookup (eg, react class components) need to treat the `props` member as an input
2481724830
// instead of an output position when resolving the signature. We need to go back to the input signatures of the composite signature,
2481824831
// get the type of `props` on each return type individually, and then _intersect them_, rather than union them (as would normally occur
2481924832
// 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.
2482024833
// The default behavior of `getTypeOfFirstParameterOfSignatureWithFallback` when no `props` member name is defined is much more sane.
2482124834
const results: Type[] = [];
24822-
for (const signature of sig.unionSignatures) {
24835+
for (const signature of sig.compositeSignatures) {
2482324836
const instance = getReturnTypeOfSignature(signature);
2482424837
if (isTypeAny(instance)) {
2482524838
return instance;
@@ -24830,7 +24843,7 @@ namespace ts {
2483024843
}
2483124844
results.push(propType);
2483224845
}
24833-
return getIntersectionType(results);
24846+
return getIntersectionType(results); // Same result for both union and intersection signatures
2483424847
}
2483524848
const instanceType = getReturnTypeOfSignature(sig);
2483624849
return isTypeAny(instanceType) ? instanceType : getTypeOfPropertyOfType(instanceType, forcedLookupLocation);
@@ -25006,9 +25019,10 @@ namespace ts {
2500625019
minArgCount,
2500725020
(left.flags | right.flags) & SignatureFlags.PropagatingFlags
2500825021
);
25009-
result.unionSignatures = concatenate(left.unionSignatures || [left], [right]);
25022+
result.compositeKind = TypeFlags.Intersection;
25023+
result.compositeSignatures = concatenate(left.compositeKind === TypeFlags.Intersection && left.compositeSignatures || [left], [right]);
2501025024
if (paramMapper) {
25011-
result.mapper = left.mapper && left.unionSignatures ? combineTypeMappers(left.mapper, paramMapper) : paramMapper;
25025+
result.mapper = left.compositeKind === TypeFlags.Intersection && left.mapper && left.compositeSignatures ? combineTypeMappers(left.mapper, paramMapper) : paramMapper;
2501225026
}
2501325027
return result;
2501425028
}

src/compiler/types.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -5554,7 +5554,9 @@ namespace ts {
55545554
/* @internal */
55555555
mapper?: TypeMapper; // Instantiation mapper
55565556
/* @internal */
5557-
unionSignatures?: Signature[]; // Underlying signatures of a union signature
5557+
compositeSignatures?: Signature[]; // Underlying signatures of a union/intersection signature
5558+
/* @internal */
5559+
compositeKind?: TypeFlags; // TypeFlags.Union if the underlying signatures are from union members, otherwise TypeFlags.Intersection
55585560
/* @internal */
55595561
erasedSignatureCache?: Signature; // Erased version of signature (deferred)
55605562
/* @internal */

0 commit comments

Comments
 (0)