Merge pull request #12537 from Microsoft/fixIntersectionNormalization

Deduplicate intersections before distributing over unions
This commit is contained in:
Anders Hejlsberg 2016-11-28 10:23:53 -08:00 committed by GitHub
commit 5b873fa041
5 changed files with 328 additions and 9 deletions

View File

@ -5637,6 +5637,7 @@ namespace ts {
containsString?: boolean;
containsNumber?: boolean;
containsStringOrNumberLiteral?: boolean;
unionIndex?: number;
}
function binarySearchTypes(types: Type[], type: Type): number {
@ -5831,6 +5832,9 @@ namespace ts {
typeSet.containsAny = true;
}
else if (!(type.flags & TypeFlags.Never) && (strictNullChecks || !(type.flags & TypeFlags.Nullable)) && !contains(typeSet, type)) {
if (type.flags & TypeFlags.Union && typeSet.unionIndex === undefined) {
typeSet.unionIndex = typeSet.length;
}
typeSet.push(type);
}
}
@ -5857,15 +5861,6 @@ 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.
return getUnionType(map((<UnionType>type).types, t => getIntersectionType(replaceElement(types, i, t))),
/*subtypeReduction*/ false, aliasSymbol, aliasTypeArguments);
}
}
const typeSet = [] as TypeSet;
addTypesToIntersection(typeSet, types);
if (typeSet.containsAny) {
@ -5874,6 +5869,14 @@ namespace ts {
if (typeSet.length === 1) {
return typeSet[0];
}
const unionIndex = typeSet.unionIndex;
if (unionIndex !== undefined) {
// 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.
const unionType = <UnionType>typeSet[unionIndex];
return getUnionType(map(unionType.types, t => getIntersectionType(replaceElement(typeSet, unionIndex, t))),
/*subtypeReduction*/ false, aliasSymbol, aliasTypeArguments);
}
const id = getTypeListId(typeSet);
let type = intersectionTypes[id];
if (!type) {

View File

@ -58,6 +58,51 @@ function getValueAsString(value: IntersectionFail): string {
return '' + value.num;
}
return value.str;
}
// Repro from #12535
namespace enums {
export const enum A {
a1,
a2,
a3,
// ... elements omitted for the sake of clarity
a75,
a76,
a77,
}
export const enum B {
b1,
b2,
// ... elements omitted for the sake of clarity
b86,
b87,
}
export const enum C {
c1,
c2,
// ... elements omitted for the sake of clarity
c210,
c211,
}
export type Genre = A | B | C;
}
type Foo = {
genreId: enums.Genre;
};
type Bar = {
genreId: enums.Genre;
};
type FooBar = Foo & Bar;
function foo(so: any) {
const val = so as FooBar;
const isGenre = val.genreId;
return isGenre;
}
//// [intersectionTypeNormalization.js]
@ -77,3 +122,8 @@ function getValueAsString(value) {
}
return value.str;
}
function foo(so) {
var val = so;
var isGenre = val.genreId;
return isGenre;
}

View File

@ -240,3 +240,113 @@ function getValueAsString(value: IntersectionFail): string {
>value : Symbol(value, Decl(intersectionTypeNormalization.ts, 54, 26))
>str : Symbol(str, Decl(intersectionTypeNormalization.ts, 47, 35))
}
// Repro from #12535
namespace enums {
>enums : Symbol(enums, Decl(intersectionTypeNormalization.ts, 59, 1))
export const enum A {
>A : Symbol(A, Decl(intersectionTypeNormalization.ts, 63, 17))
a1,
>a1 : Symbol(A.a1, Decl(intersectionTypeNormalization.ts, 64, 25))
a2,
>a2 : Symbol(A.a2, Decl(intersectionTypeNormalization.ts, 65, 11))
a3,
>a3 : Symbol(A.a3, Decl(intersectionTypeNormalization.ts, 66, 11))
// ... elements omitted for the sake of clarity
a75,
>a75 : Symbol(A.a75, Decl(intersectionTypeNormalization.ts, 67, 11))
a76,
>a76 : Symbol(A.a76, Decl(intersectionTypeNormalization.ts, 69, 12))
a77,
>a77 : Symbol(A.a77, Decl(intersectionTypeNormalization.ts, 70, 12))
}
export const enum B {
>B : Symbol(B, Decl(intersectionTypeNormalization.ts, 72, 5))
b1,
>b1 : Symbol(B.b1, Decl(intersectionTypeNormalization.ts, 73, 25))
b2,
>b2 : Symbol(B.b2, Decl(intersectionTypeNormalization.ts, 74, 11))
// ... elements omitted for the sake of clarity
b86,
>b86 : Symbol(B.b86, Decl(intersectionTypeNormalization.ts, 75, 11))
b87,
>b87 : Symbol(B.b87, Decl(intersectionTypeNormalization.ts, 77, 12))
}
export const enum C {
>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 79, 5))
c1,
>c1 : Symbol(C.c1, Decl(intersectionTypeNormalization.ts, 80, 25))
c2,
>c2 : Symbol(C.c2, Decl(intersectionTypeNormalization.ts, 81, 11))
// ... elements omitted for the sake of clarity
c210,
>c210 : Symbol(C.c210, Decl(intersectionTypeNormalization.ts, 82, 11))
c211,
>c211 : Symbol(C.c211, Decl(intersectionTypeNormalization.ts, 84, 13))
}
export type Genre = A | B | C;
>Genre : Symbol(Genre, Decl(intersectionTypeNormalization.ts, 86, 5))
>A : Symbol(A, Decl(intersectionTypeNormalization.ts, 63, 17))
>B : Symbol(B, Decl(intersectionTypeNormalization.ts, 72, 5))
>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 79, 5))
}
type Foo = {
>Foo : Symbol(Foo, Decl(intersectionTypeNormalization.ts, 88, 1))
genreId: enums.Genre;
>genreId : Symbol(genreId, Decl(intersectionTypeNormalization.ts, 90, 12))
>enums : Symbol(enums, Decl(intersectionTypeNormalization.ts, 59, 1))
>Genre : Symbol(enums.Genre, Decl(intersectionTypeNormalization.ts, 86, 5))
};
type Bar = {
>Bar : Symbol(Bar, Decl(intersectionTypeNormalization.ts, 92, 2))
genreId: enums.Genre;
>genreId : Symbol(genreId, Decl(intersectionTypeNormalization.ts, 94, 12))
>enums : Symbol(enums, Decl(intersectionTypeNormalization.ts, 59, 1))
>Genre : Symbol(enums.Genre, Decl(intersectionTypeNormalization.ts, 86, 5))
};
type FooBar = Foo & Bar;
>FooBar : Symbol(FooBar, Decl(intersectionTypeNormalization.ts, 96, 2))
>Foo : Symbol(Foo, Decl(intersectionTypeNormalization.ts, 88, 1))
>Bar : Symbol(Bar, Decl(intersectionTypeNormalization.ts, 92, 2))
function foo(so: any) {
>foo : Symbol(foo, Decl(intersectionTypeNormalization.ts, 98, 24))
>so : Symbol(so, Decl(intersectionTypeNormalization.ts, 100, 13))
const val = so as FooBar;
>val : Symbol(val, Decl(intersectionTypeNormalization.ts, 101, 9))
>so : Symbol(so, Decl(intersectionTypeNormalization.ts, 100, 13))
>FooBar : Symbol(FooBar, Decl(intersectionTypeNormalization.ts, 96, 2))
const isGenre = val.genreId;
>isGenre : Symbol(isGenre, Decl(intersectionTypeNormalization.ts, 102, 9))
>val.genreId : Symbol(genreId, Decl(intersectionTypeNormalization.ts, 90, 12), Decl(intersectionTypeNormalization.ts, 94, 12))
>val : Symbol(val, Decl(intersectionTypeNormalization.ts, 101, 9))
>genreId : Symbol(genreId, Decl(intersectionTypeNormalization.ts, 90, 12), Decl(intersectionTypeNormalization.ts, 94, 12))
return isGenre;
>isGenre : Symbol(isGenre, Decl(intersectionTypeNormalization.ts, 102, 9))
}

View File

@ -244,3 +244,114 @@ function getValueAsString(value: IntersectionFail): string {
>value : { kind: "string"; str: string; } & ToString
>str : string
}
// Repro from #12535
namespace enums {
>enums : typeof enums
export const enum A {
>A : A
a1,
>a1 : A.a1
a2,
>a2 : A.a2
a3,
>a3 : A.a3
// ... elements omitted for the sake of clarity
a75,
>a75 : A.a75
a76,
>a76 : A.a76
a77,
>a77 : A.a77
}
export const enum B {
>B : B
b1,
>b1 : B.b1
b2,
>b2 : B.b2
// ... elements omitted for the sake of clarity
b86,
>b86 : B.b86
b87,
>b87 : B.b87
}
export const enum C {
>C : C
c1,
>c1 : C.c1
c2,
>c2 : C.c2
// ... elements omitted for the sake of clarity
c210,
>c210 : C.c210
c211,
>c211 : C.c211
}
export type Genre = A | B | C;
>Genre : Genre
>A : A
>B : B
>C : C
}
type Foo = {
>Foo : Foo
genreId: enums.Genre;
>genreId : enums.Genre
>enums : any
>Genre : enums.Genre
};
type Bar = {
>Bar : Bar
genreId: enums.Genre;
>genreId : enums.Genre
>enums : any
>Genre : enums.Genre
};
type FooBar = Foo & Bar;
>FooBar : FooBar
>Foo : Foo
>Bar : Bar
function foo(so: any) {
>foo : (so: any) => enums.Genre
>so : any
const val = so as FooBar;
>val : FooBar
>so as FooBar : FooBar
>so : any
>FooBar : FooBar
const isGenre = val.genreId;
>isGenre : enums.Genre
>val.genreId : enums.Genre
>val : FooBar
>genreId : enums.Genre
return isGenre;
>isGenre : enums.Genre
}

View File

@ -57,4 +57,49 @@ function getValueAsString(value: IntersectionFail): string {
return '' + value.num;
}
return value.str;
}
// Repro from #12535
namespace enums {
export const enum A {
a1,
a2,
a3,
// ... elements omitted for the sake of clarity
a75,
a76,
a77,
}
export const enum B {
b1,
b2,
// ... elements omitted for the sake of clarity
b86,
b87,
}
export const enum C {
c1,
c2,
// ... elements omitted for the sake of clarity
c210,
c211,
}
export type Genre = A | B | C;
}
type Foo = {
genreId: enums.Genre;
};
type Bar = {
genreId: enums.Genre;
};
type FooBar = Foo & Bar;
function foo(so: any) {
const val = so as FooBar;
const isGenre = val.genreId;
return isGenre;
}