From 6a0f72916eb31ded2a011dd9dd4e701145b85d78 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 18 Oct 2016 14:13:19 -0700 Subject: [PATCH] Simplify logic in checkTypeRelatedTo --- src/compiler/checker.ts | 57 ++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 879e2dec9df..c395343ea35 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5622,7 +5622,6 @@ namespace ts { if (type.flags & TypeFlags.Union) { // We are attempting to construct a type of the form X & (A | B) & Y. Transform this into a type of // the form X & A & Y | X & B & Y and recursively reduce until no union type constituents remain. - let unionType = types[i]; return getUnionType(map((type).types, t => getIntersectionType(replaceElement(types, i, t))), /*subtypeReduction*/ false, aliasSymbol, aliasTypeArguments); } @@ -6574,7 +6573,9 @@ namespace ts { const saveErrorInfo = errorInfo; - // Note that these checks are specifically ordered to produce correct results. + // Note that these checks are specifically ordered to produce correct results. In particular, + // we need to deconstruct unions before intersections (because unions are always at the top), + // and we need to handle "each" relations before "some" relations for the same kind of type. if (source.flags & TypeFlags.Union) { if (relation === comparableRelation) { result = someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive)); @@ -6582,44 +6583,36 @@ namespace ts { else { result = eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive)); } - if (result) { return result; } } + else if (target.flags & TypeFlags.Union) { + if (result = typeRelatedToSomeType(source, target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive))) { + return result; + } + } else if (target.flags & TypeFlags.Intersection) { - result = typeRelatedToEachType(source, target as IntersectionType, reportErrors); - - if (result) { + if (result = typeRelatedToEachType(source, target as IntersectionType, reportErrors)) { return result; } } - else { - // It is necessary to try these "some" checks on both sides because there may be nested "each" checks - // on either side that need to be prioritized. For example, A | B = (A | B) & (C | D) or - // A & B = (A & B) | (C & D). - if (source.flags & TypeFlags.Intersection) { - // Check to see if any constituents of the intersection are immediately related to the target. - // - // Don't report errors though. Checking whether a constituent is related to the source is not actually - // useful and leads to some confusing error messages. Instead it is better to let the below checks - // take care of this, or to not elaborate at all. For instance, - // - // - For an object type (such as 'C = A & B'), users are usually more interested in structural errors. - // - // - For a union type (such as '(A | B) = (C & D)'), it's better to hold onto the whole intersection - // than to report that 'D' is not assignable to 'A' or 'B'. - // - // - For a primitive type or type parameter (such as 'number = A & B') there is no point in - // breaking the intersection apart. - if (result = someTypeRelatedToType(source, target, /*reportErrors*/ false)) { - return result; - } - } - if (target.flags & TypeFlags.Union) { - if (result = typeRelatedToSomeType(source, target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive))) { - return result; - } + else if (source.flags & TypeFlags.Intersection) { + // Check to see if any constituents of the intersection are immediately related to the target. + // + // Don't report errors though. Checking whether a constituent is related to the source is not actually + // useful and leads to some confusing error messages. Instead it is better to let the below checks + // take care of this, or to not elaborate at all. For instance, + // + // - For an object type (such as 'C = A & B'), users are usually more interested in structural errors. + // + // - For a union type (such as '(A | B) = (C & D)'), it's better to hold onto the whole intersection + // than to report that 'D' is not assignable to 'A' or 'B'. + // + // - For a primitive type or type parameter (such as 'number = A & B') there is no point in + // breaking the intersection apart. + if (result = someTypeRelatedToType(source, target, /*reportErrors*/ false)) { + return result; } }