Skip to content

Commit c8423d3

Browse files
committed
Getting rid of subtype reduction for union types
1 parent 7dbb69a commit c8423d3

File tree

1 file changed

+40
-71
lines changed

1 file changed

+40
-71
lines changed

src/compiler/checker.ts

Lines changed: 40 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -3050,42 +3050,55 @@ namespace ts {
30503050
setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexType, arrayType.numberIndexType);
30513051
}
30523052

3053-
function signatureListsIdentical(s: Signature[], t: Signature[]): boolean {
3054-
if (s.length !== t.length) {
3055-
return false;
3053+
function findMatchingSignature(signature: Signature, signatureList: Signature[]): Signature {
3054+
for (let s of signatureList) {
3055+
// Only signatures with no type parameters may differ in return types
3056+
if (compareSignatures(signature, s, /*compareReturnTypes*/ !!signature.typeParameters, compareTypes)) {
3057+
return s;
3058+
}
30563059
}
3057-
for (let i = 0; i < s.length; i++) {
3058-
if (!compareSignatures(s[i], t[i], /*compareReturnTypes*/ false, compareTypes)) {
3059-
return false;
3060+
}
3061+
3062+
function findMatchingSignatures(signature: Signature, signatureLists: Signature[][]): Signature[] {
3063+
let result: Signature[] = undefined;
3064+
for (let i = 1; i < signatureLists.length; i++) {
3065+
let match = findMatchingSignature(signature, signatureLists[i]);
3066+
if (!match) {
3067+
return undefined;
3068+
}
3069+
if (!result) {
3070+
result = [signature];
3071+
}
3072+
if (match !== signature) {
3073+
result.push(match);
30603074
}
30613075
}
3062-
return true;
3076+
return result;
30633077
}
30643078

3065-
// If the lists of call or construct signatures in the given types are all identical except for return types,
3066-
// and if none of the signatures are generic, return a list of signatures that has substitutes a union of the
3067-
// return types of the corresponding signatures in each resulting signature.
3079+
// The signatures of a union type are those signatures that are present and identical in each of the
3080+
// constituent types, except that non-generic signatures may differ in return types. When signatures
3081+
// differ in return types, the resulting return type is the union of the constituent return types.
30683082
function getUnionSignatures(types: Type[], kind: SignatureKind): Signature[] {
30693083
let signatureLists = map(types, t => getSignaturesOfType(t, kind));
3070-
let signatures = signatureLists[0];
3071-
for (let signature of signatures) {
3072-
if (signature.typeParameters) {
3073-
return emptyArray;
3074-
}
3075-
}
3076-
for (let i = 1; i < signatureLists.length; i++) {
3077-
if (!signatureListsIdentical(signatures, signatureLists[i])) {
3078-
return emptyArray;
3084+
let result: Signature[] = undefined;
3085+
for (let source of signatureLists[0]) {
3086+
let unionSignatures = findMatchingSignatures(source, signatureLists);
3087+
if (unionSignatures) {
3088+
let signature: Signature = undefined;
3089+
if (unionSignatures.length === 1 || source.typeParameters) {
3090+
signature = source;
3091+
}
3092+
else {
3093+
signature = cloneSignature(source);
3094+
// Clear resolved return type we possibly got from cloneSignature
3095+
signature.resolvedReturnType = undefined;
3096+
signature.unionSignatures = unionSignatures;
3097+
}
3098+
(result || (result = [])).push(signature);
30793099
}
30803100
}
3081-
let result = map(signatures, cloneSignature);
3082-
for (var i = 0; i < result.length; i++) {
3083-
let s = result[i];
3084-
// Clear resolved return type we possibly got from cloneSignature
3085-
s.resolvedReturnType = undefined;
3086-
s.unionSignatures = map(signatureLists, signatures => signatures[i]);
3087-
}
3088-
return result;
3101+
return result || emptyArray;
30893102
}
30903103

30913104
function getUnionIndexType(types: Type[], kind: IndexKind): Type {
@@ -3245,9 +3258,6 @@ namespace ts {
32453258
* type itself. Note that the apparent type of a union type is the union type itself.
32463259
*/
32473260
function getApparentType(type: Type): Type {
3248-
if (type.flags & TypeFlags.Union) {
3249-
type = getReducedTypeOfUnionType(<UnionType>type);
3250-
}
32513261
if (type.flags & TypeFlags.TypeParameter) {
32523262
do {
32533263
type = getConstraintOfTypeParameter(<TypeParameter>type);
@@ -4104,47 +4114,6 @@ namespace ts {
41044114
return type;
41054115
}
41064116

4107-
function isTypeSubtypeOfSomeType(candidate: Type, types: Type[]): boolean {
4108-
for (let type of types) {
4109-
if (candidate !== type && isTypeSubtypeOf(candidate, type)) {
4110-
return true;
4111-
}
4112-
}
4113-
return false;
4114-
}
4115-
4116-
function removeSubtypes(types: Type[]): Type[] {
4117-
let result = types;
4118-
let i = result.length;
4119-
while (i > 0) {
4120-
i--;
4121-
if (isTypeSubtypeOfSomeType(result[i], result)) {
4122-
if (result === types) {
4123-
result = types.slice(0);
4124-
}
4125-
result.splice(i, 1);
4126-
}
4127-
}
4128-
return result;
4129-
}
4130-
4131-
// The reduced type is a union type in which no constituent type is a subtype of another
4132-
// constituent type.
4133-
function getReducedTypeOfUnionType(type: UnionType): Type {
4134-
if (!type.reducedType) {
4135-
type.reducedType = circularType;
4136-
let typesWithoutSubtypes = removeSubtypes(type.types);
4137-
let reducedType = typesWithoutSubtypes === type.types ? type : getUnionType(typesWithoutSubtypes);
4138-
if (type.reducedType === circularType) {
4139-
type.reducedType = reducedType;
4140-
}
4141-
}
4142-
else if (type.reducedType === circularType) {
4143-
type.reducedType = type;
4144-
}
4145-
return type.reducedType;
4146-
}
4147-
41484117
function getTypeFromUnionTypeNode(node: UnionTypeNode): Type {
41494118
let links = getNodeLinks(node);
41504119
if (!links.resolvedType) {

0 commit comments

Comments
 (0)