From ef5f3c90a457ba50484da59000c39a2797150240 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 18 Oct 2016 11:53:26 -0700 Subject: [PATCH] Normalize intersection and union types --- src/compiler/checker.ts | 15 +++++++++++++++ src/compiler/core.ts | 6 ++++++ 2 files changed, 21 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d3f15f2367b..879e2dec9df 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5603,6 +5603,11 @@ namespace ts { } } + // We normalize combinations of intersection and union types based on the distributive property of the '&' + // operator. Specifically, because X & (A | B) is equivalent to X & A | X & B, we can transform intersection + // types with union type constituents into equivalent union types with intersection type constituents and + // effectively ensure that union types are always at the top level in type representations. + // // We do not perform structural deduplication on intersection types. Intersection types are created only by the & // type operator and we can't reduce those because we want to support recursive intersection types. For example, // a type alias of the form "type List = T & { next: List }" cannot be reduced during its declaration. @@ -5612,6 +5617,16 @@ namespace ts { if (types.length === 0) { return emptyObjectType; } + for (let i = 0; i < types.length; i++) { + const type = types[i]; + 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); + } + } const typeSet = [] as TypeSet; addTypesToIntersection(typeSet, types); if (typeSet.containsAny) { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index befa30b6006..b9a3becba72 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -532,6 +532,12 @@ namespace ts { : undefined; } + export function replaceElement(array: T[], index: number, value: T): T[] { + const result = array.slice(0); + result[index] = value; + return result; + } + /** * Performs a binary search, finding the index at which 'value' occurs in 'array'. * If no such index is found, returns the 2's-complement of first index at which