Skip to content

Commit

Permalink
Preserve type aliases for union and intersection types (microsoft#42149)
Browse files Browse the repository at this point in the history
* Create separate types for equivalent aliased unions

* Accept new baselines

* Preserve original types for union types

* Accept new baselines

* Preserve intersection origin for union types

* Accept new baselines

* Accept new baselines

* Preserve aliases during relationship checks

* Accept new baselines

* Preserve aliases for intersection and indexed access types

* Accept new baselines

* Compute intersection-of-unions cross product without recursion

* Accept new baselines

* Use denormalized type objects for origin / support 'keyof' origins

* Accept new baselines

* Fix fourslash test

* Recursively extract named union types

* Accept new baselines

* Map on union origin in mapType to better preserve aliases and origins

* Remove redundant call

* Accept new baselines

* Revert back to declared type when branches produce equivalent union

* Accept new baselines

* Don't include denormal origin types in regular type statistics

* Fix issue with unions not being marked primitive-only

* Allow new alias to be associated with type alias instantiation

* Accept new baselines

* Revert "Accept new baselines"

This reverts commit 4507270.

* Revert "Allow new alias to be associated with type alias instantiation"

This reverts commit 2c2d06d.
  • Loading branch information
ahejlsberg authored and Zzzen committed Jan 16, 2021
1 parent dfd7d97 commit 49d0f63
Show file tree
Hide file tree
Showing 142 changed files with 1,071 additions and 958 deletions.
218 changes: 161 additions & 57 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5008,7 +5008,7 @@ namespace ts {
// This *should* be every type other than null, undefined, void, and never
Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive,
/* @internal */
NotPrimitiveUnion = Any | Unknown | Enum | Void | Never | StructuredOrInstantiable,
NotPrimitiveUnion = Any | Unknown | Enum | Void | Never | Object | Intersection | Instantiable,
// The following flags are aggregated during union and intersection type construction
/* @internal */
IncludesMask = Any | Unknown | Primitive | Never | Object | Union | Intersection | NonPrimitive | TemplateLiteral,
Expand Down Expand Up @@ -5296,9 +5296,11 @@ namespace ts {

export interface UnionType extends UnionOrIntersectionType {
/* @internal */
resolvedReducedType: Type;
resolvedReducedType?: Type;
/* @internal */
regularType: UnionType;
regularType?: UnionType;
/* @internal */
origin?: Type; // Denormalized union, intersection, or index type in which union originates
}

export interface IntersectionType extends UnionOrIntersectionType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ animal.canMeow; // since is cat, should not be an error
>canMeow : true

const animalOrUndef = { type: 'cat', canMeow: true } as Animal | undefined;
>animalOrUndef : Cat | Dog | undefined
>{ type: 'cat', canMeow: true } as Animal | undefined : Cat | Dog | undefined
>animalOrUndef : Animal | undefined
>{ type: 'cat', canMeow: true } as Animal | undefined : Animal | undefined
>{ type: 'cat', canMeow: true } : { type: "cat"; canMeow: true; }
>type : "cat"
>'cat' : "cat"
Expand All @@ -61,7 +61,7 @@ assertEqual(animalOrUndef?.type, 'cat' as const);
>assertEqual(animalOrUndef?.type, 'cat' as const) : void
>assertEqual : <T>(value: any, type: T) => asserts value is T
>animalOrUndef?.type : "cat" | "dog" | undefined
>animalOrUndef : Cat | Dog | undefined
>animalOrUndef : Animal | undefined
>type : "cat" | "dog" | undefined
>'cat' as const : "cat"
>'cat' : "cat"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts(58,5): error TS2322: Type 'S' is not assignable to type 'T'.
Property 'c' is missing in type 'S' but required in type '{ a: 2; b: 4 | 3; c: string; }'.
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts(82,5): error TS2322: Type 'S' is not assignable to type 'T'.
Type 'S' is not assignable to type '{ a: 0 | 2 | 1; b: 0 | 2 | 1; c: 2; }'.
Type 'S' is not assignable to type '{ a: N; b: N; c: 2; }'.
Types of property 'c' are incompatible.
Type '0 | 2 | 1' is not assignable to type '2'.
Type 'N' is not assignable to type '2'.
Type '0' is not assignable to type '2'.


Expand Down Expand Up @@ -107,9 +107,9 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
t = s;
~
!!! error TS2322: Type 'S' is not assignable to type 'T'.
!!! error TS2322: Type 'S' is not assignable to type '{ a: 0 | 2 | 1; b: 0 | 2 | 1; c: 2; }'.
!!! error TS2322: Type 'S' is not assignable to type '{ a: N; b: N; c: 2; }'.
!!! error TS2322: Types of property 'c' are incompatible.
!!! error TS2322: Type '0 | 2 | 1' is not assignable to type '2'.
!!! error TS2322: Type 'N' is not assignable to type '2'.
!!! error TS2322: Type '0' is not assignable to type '2'.
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,58 +155,58 @@ namespace Example5 {
// 3 discriminant properties with 3 types a piece
// is 27 possible combinations.
type N = 0 | 1 | 2;
>N : 0 | 2 | 1
>N : N

type S = { a: N, b: N, c: N };
>S : S
>a : 0 | 2 | 1
>b : 0 | 2 | 1
>c : 0 | 2 | 1
>a : N
>b : N
>c : N

type T = { a: 0, b: N, c: N }
>T : T
>a : 0
>b : 0 | 2 | 1
>c : 0 | 2 | 1
>b : N
>c : N

| { a: 1, b: N, c: N }
>a : 1
>b : 0 | 2 | 1
>c : 0 | 2 | 1
>b : N
>c : N

| { a: 2, b: N, c: N }
>a : 2
>b : 0 | 2 | 1
>c : 0 | 2 | 1
>b : N
>c : N

| { a: N, b: 0, c: N }
>a : 0 | 2 | 1
>a : N
>b : 0
>c : 0 | 2 | 1
>c : N

| { a: N, b: 1, c: N }
>a : 0 | 2 | 1
>a : N
>b : 1
>c : 0 | 2 | 1
>c : N

| { a: N, b: 2, c: N }
>a : 0 | 2 | 1
>a : N
>b : 2
>c : 0 | 2 | 1
>c : N

| { a: N, b: N, c: 0 }
>a : 0 | 2 | 1
>b : 0 | 2 | 1
>a : N
>b : N
>c : 0

| { a: N, b: N, c: 1 }
>a : 0 | 2 | 1
>b : 0 | 2 | 1
>a : N
>b : N
>c : 1

| { a: N, b: N, c: 2 };
>a : 0 | 2 | 1
>b : 0 | 2 | 1
>a : N
>b : N
>c : 2

declare let s: S;
Expand Down Expand Up @@ -359,9 +359,9 @@ namespace GH12052 {

good.type = getAxisType();
>good.type = getAxisType() : IAxisType
>good.type : IAxisType
>good.type : "linear" | "categorical"
>good : IAxis
>type : IAxisType
>type : "linear" | "categorical"
>getAxisType() : IAxisType
>getAxisType : () => IAxisType
}
Expand Down Expand Up @@ -473,17 +473,17 @@ namespace GH39357 {
>A : A

type B = "a" | "b" | "c";
>B : "a" | "b" | "c"
>B : B

declare const b: B;
>b : "a" | "b" | "c"
>b : B

const a: A = b === "a" || b === "b" ? [b, 1] : ["c", ""];
>a : A
>b === "a" || b === "b" ? [b, 1] : ["c", ""] : ["a" | "b", number] | ["c", string]
>b === "a" || b === "b" : boolean
>b === "a" : boolean
>b : "a" | "b" | "c"
>b : B
>"a" : "a"
>b === "b" : boolean
>b : "b" | "c"
Expand Down
12 changes: 6 additions & 6 deletions tests/baselines/reference/booleanLiteralTypes1.types
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
=== tests/cases/conformance/types/literal/booleanLiteralTypes1.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
>a : A1

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

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
12 changes: 6 additions & 6 deletions tests/baselines/reference/booleanLiteralTypes2.types
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
=== 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
>a : A1

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

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
8 changes: 4 additions & 4 deletions tests/baselines/reference/callsOnComplexSignatures.types
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ function test1() {
>t : Temp1 | Temp2

const z = t.getValue("bar"); // Should be fine
>z : React.ReactText
>t.getValue("bar") : React.ReactText
>z : string | number
>t.getValue("bar") : string | number
>t.getValue : ((name: "foo" | "bar") => number) | ((name: "bar" | "baz") => string)
>t : Temp1 | Temp2
>getValue : ((name: "foo" | "bar") => number) | ((name: "bar" | "baz") => string)
Expand Down Expand Up @@ -399,7 +399,7 @@ function test5() {
}

var C: React.ComponentType<P1> | React.ComponentType<P2> = null as any;
>C : React.ComponentClass<P1, any> | React.StatelessComponent<P1> | React.ComponentClass<P2, any> | React.StatelessComponent<P2>
>C : React.ComponentType<P1> | React.ComponentType<P2>
>React : any
>React : any
>null as any : any
Expand All @@ -408,7 +408,7 @@ function test5() {
const a = <C p={true} />;
>a : JSX.Element
><C p={true} /> : JSX.Element
>C : React.ComponentClass<P1, any> | React.StatelessComponent<P1> | React.ComponentClass<P2, any> | React.StatelessComponent<P2>
>C : React.ComponentType<P1> | React.ComponentType<P2>
>p : true
>true : true
}
Expand Down
Loading

0 comments on commit 49d0f63

Please sign in to comment.