mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-14 02:15:12 -06:00
Merge pull request #12537 from Microsoft/fixIntersectionNormalization
Deduplicate intersections before distributing over unions
This commit is contained in:
commit
5b873fa041
@ -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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user