Skip to content

Don't use a cached union/intersection type if there is an aliasSymbol #17349

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 26 additions & 6 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7344,17 +7344,27 @@ namespace ts {
if (types.length === 1) {
return types[0];
}
if (aliasSymbol) {
// Don't reuse types that would have different `aliasSymbol`s.
return create();
}

const id = getTypeListId(types);
let type = unionTypes.get(id);
if (!type) {
const propagatedFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable);
type = <UnionType>createType(TypeFlags.Union | propagatedFlags);
type = create();
unionTypes.set(id, type);
}
return type;

function create() {
const propagatedFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable);
const type = <UnionType>createType(TypeFlags.Union | propagatedFlags);
type.types = types;
type.aliasSymbol = aliasSymbol;
type.aliasTypeArguments = aliasTypeArguments;
return type;
}
return type;
}

function getTypeFromUnionTypeNode(node: UnionTypeNode): Type {
Expand Down Expand Up @@ -7437,17 +7447,27 @@ namespace ts {
return getUnionType(map(unionType.types, t => getIntersectionType(replaceElement(typeSet, unionIndex, t))),
/*subtypeReduction*/ false, aliasSymbol, aliasTypeArguments);
}
if (aliasSymbol) {
// Don't reuse types that would have different `aliasSymbol`s.
return create();
}

const id = getTypeListId(typeSet);
let type = intersectionTypes.get(id);
if (!type) {
const propagatedFlags = getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Nullable);
type = <IntersectionType>createType(TypeFlags.Intersection | propagatedFlags);
type = create();
intersectionTypes.set(id, type);
}
return type;

function create(): IntersectionType {
const propagatedFlags = getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Nullable);
const type = <IntersectionType>createType(TypeFlags.Intersection | propagatedFlags);
type.types = typeSet;
type.aliasSymbol = aliasSymbol;
type.aliasTypeArguments = aliasTypeArguments;
return type;
}
return type;
}

function getTypeFromIntersectionTypeNode(node: IntersectionTypeNode): Type {
Expand Down
16 changes: 8 additions & 8 deletions tests/baselines/reference/booleanLiteralTypes1.types
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
=== tests/cases/conformance/types/literal/booleanLiteralTypes1.ts ===
type A1 = true | false;
>A1 : boolean
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This behaviour is intended. I don't know that it's super important. It's probably even less important that

type A = { a }
type B = { b }
type T = A | B
type U = A | B
declare var t: T
declare var u: U

should also print either T or U (or A | B) for both t and u.

It's more important that:

declare var x: true | false

prints 'boolean' for the type of x

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ahejlsberg can you weigh in on true | false and boolean being equivalent to type B = true | false ?

>A1 : A1
>true : true
>false : false

type A2 = false | true;
>A2 : boolean
>A2 : A2
>false : false
>true : true

function f1() {
>f1 : () => void

var a: A1;
>a : boolean
>A1 : boolean
>a : A1
>A1 : A1

var a: A2;
>a : boolean
>A2 : boolean
>a : A1
>A2 : A2

var a: true | false;
>a : boolean
>a : A1
>true : true
>false : false

var a: false | true;
>a : boolean
>a : A1
Copy link
Author

@ghost ghost Jul 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change looked wrong until I realized that this is the fourth declaration of a, and the first declaration has type A1, which is the type shown here.
There is a test below showing that a variable whose only declaration is of type true | false will still print as boolean.

>false : false
>true : true
}
Expand Down
16 changes: 8 additions & 8 deletions tests/baselines/reference/booleanLiteralTypes2.types
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
=== tests/cases/conformance/types/literal/booleanLiteralTypes2.ts ===
type A1 = true | false;
>A1 : boolean
>A1 : A1
>true : true
>false : false

type A2 = false | true;
>A2 : boolean
>A2 : A2
>false : false
>true : true

function f1() {
>f1 : () => void

var a: A1;
>a : boolean
>A1 : boolean
>a : A1
>A1 : A1

var a: A2;
>a : boolean
>A2 : boolean
>a : A1
>A2 : A2

var a: true | false;
>a : boolean
>a : A1
>true : true
>false : false

var a: false | true;
>a : boolean
>a : A1
>false : false
>true : true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,5 @@ exports.v = v;
//// [0.d.ts]
export declare type Data = string | boolean;
//// [1.d.ts]
import * as Z from "./0";
declare let v: Z.Data;
declare let v: string | boolean;
export { v };
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import * as Z from "./0"

//let v2: Z.Data;
let v = "str" || true;
>v : Z.Data
>v : string | boolean
>"str" || true : true | "str"
>"str" : "str"
>true : true

export { v }
>v : Z.Data
>v : string | boolean

54 changes: 27 additions & 27 deletions tests/baselines/reference/enumLiteralTypes1.types
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ type YesNo = Choice.Yes | Choice.No;
>No : Choice.No

type NoYes = Choice.No | Choice.Yes;
>NoYes : YesNo
>NoYes : NoYes
>Choice : any
>No : Choice.No
>Choice : any
>Yes : Choice.Yes

type UnknownYesNo = Choice.Unknown | Choice.Yes | Choice.No;
>UnknownYesNo : Choice
>UnknownYesNo : UnknownYesNo
>Choice : any
>Unknown : Choice.Unknown
>Choice : any
Expand All @@ -37,7 +37,7 @@ function f1() {

var a: NoYes;
>a : YesNo
>NoYes : YesNo
>NoYes : NoYes

var a: Choice.Yes | Choice.No;
>a : YesNo
Expand All @@ -55,17 +55,17 @@ function f1() {
}

function f2(a: YesNo, b: UnknownYesNo, c: Choice) {
>f2 : (a: YesNo, b: Choice, c: Choice) => void
>f2 : (a: YesNo, b: UnknownYesNo, c: Choice) => void
>a : YesNo
>YesNo : YesNo
>b : Choice
>UnknownYesNo : Choice
>b : UnknownYesNo
>UnknownYesNo : UnknownYesNo
>c : Choice
>Choice : Choice

b = a;
>b = a : YesNo
>b : Choice
>b : UnknownYesNo
>a : YesNo

c = a;
Expand All @@ -74,9 +74,9 @@ function f2(a: YesNo, b: UnknownYesNo, c: Choice) {
>a : YesNo

c = b;
>c = b : YesNo
>c = b : Choice.Yes | Choice.No
>c : Choice
>b : YesNo
>b : Choice.Yes | Choice.No
}

function f3(a: Choice.Yes, b: YesNo) {
Expand Down Expand Up @@ -234,11 +234,11 @@ declare function g(x: Choice): number;
>Choice : Choice

function f5(a: YesNo, b: UnknownYesNo, c: Choice) {
>f5 : (a: YesNo, b: Choice, c: Choice) => void
>f5 : (a: YesNo, b: UnknownYesNo, c: Choice) => void
>a : YesNo
>YesNo : YesNo
>b : Choice
>UnknownYesNo : Choice
>b : UnknownYesNo
>UnknownYesNo : UnknownYesNo
>c : Choice
>Choice : Choice

Expand Down Expand Up @@ -268,7 +268,7 @@ function f5(a: YesNo, b: UnknownYesNo, c: Choice) {
>z4 : number
>g(b) : number
>g : { (x: Choice.Yes): string; (x: Choice.No): boolean; (x: Choice): number; }
>b : Choice
>b : UnknownYesNo

var z5 = g(c);
>z5 : number
Expand Down Expand Up @@ -336,30 +336,30 @@ function f11(x: YesNo) {
}

function f12(x: UnknownYesNo) {
>f12 : (x: Choice) => void
>x : Choice
>UnknownYesNo : Choice
>f12 : (x: UnknownYesNo) => void
>x : UnknownYesNo
>UnknownYesNo : UnknownYesNo

if (x) {
>x : Choice
>x : UnknownYesNo

x;
>x : YesNo
>x : Choice.Yes | Choice.No
}
else {
x;
>x : Choice
>x : UnknownYesNo
}
}

function f13(x: UnknownYesNo) {
>f13 : (x: Choice) => void
>x : Choice
>UnknownYesNo : Choice
>f13 : (x: UnknownYesNo) => void
>x : UnknownYesNo
>UnknownYesNo : UnknownYesNo

if (x === Choice.Yes) {
>x === Choice.Yes : boolean
>x : Choice
>x : UnknownYesNo
>Choice.Yes : Choice.Yes
>Choice : typeof Choice
>Yes : Choice.Yes
Expand Down Expand Up @@ -394,9 +394,9 @@ function f20(x: Item) {
>Item : Item

switch (x.kind) {
>x.kind : YesNo
>x.kind : Choice.Yes | Choice.No
>x : Item
>kind : YesNo
>kind : Choice.Yes | Choice.No

case Choice.Yes: return x.a;
>Choice.Yes : Choice.Yes
Expand All @@ -422,9 +422,9 @@ function f21(x: Item) {
>Item : Item

switch (x.kind) {
>x.kind : YesNo
>x.kind : Choice.Yes | Choice.No
>x : Item
>kind : YesNo
>kind : Choice.Yes | Choice.No

case Choice.Yes: return x.a;
>Choice.Yes : Choice.Yes
Expand Down
Loading