Skip to content

Commit

Permalink
Merge pull request #30779 from Microsoft/relateDiscriminants
Browse files Browse the repository at this point in the history
Relate source types covered by a target discriminated union
  • Loading branch information
rbuckton authored Apr 29, 2019
2 parents de9b91f + 26fd6da commit 2d8527f
Show file tree
Hide file tree
Showing 7 changed files with 1,897 additions and 70 deletions.
274 changes: 204 additions & 70 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2284,4 +2284,29 @@ namespace ts {
}
return result;
}

export function cartesianProduct<T>(arrays: readonly T[][]) {
const result: T[][] = [];
cartesianProductWorker(arrays, result, /*outer*/ undefined, 0);
return result;
}

function cartesianProductWorker<T>(arrays: readonly (readonly T[])[], result: (readonly T[])[], outer: readonly T[] | undefined, index: number) {
for (const element of arrays[index]) {
let inner: T[];
if (outer) {
inner = outer.slice();
inner.push(element);
}
else {
inner = [element];
}
if (index === arrays.length - 1) {
result.push(inner);
}
else {
cartesianProductWorker(arrays, result, inner, index + 1);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts(44,5): error TS2322: Type 'S' is not assignable to type 'T'.
Type 'S' is not assignable to type '{ a: 2; b: 3; }'.
Types of property 'a' are incompatible.
Type '0 | 2' is not assignable to type '2'.
Type '0' is not assignable to type '2'.
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; }'.
Types of property 'c' are incompatible.
Type '0 | 2 | 1' is not assignable to type '2'.
Type '0' is not assignable to type '2'.


==== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts (3 errors) ====
// see 'typeRelatedToDiscriminatedType' in checker.ts:

// IteratorResult
namespace Example1 {
type S = { done: boolean, value: number };
type T =
| { done: true, value: number } // T0
| { done: false, value: number }; // T1

declare let s: S;
declare let t: T;

// S is assignable to T0 when S["done"] is true
// S is assignable to T1 when S["done"] is false
t = s;
}

// Dropping constituents of T
namespace Example2 {
type S = { a: 0 | 2, b: 4 };
type T = { a: 0, b: 1 | 4 } // T0
| { a: 1, b: 2 } // T1
| { a: 2, b: 3 | 4 }; // T2
declare let s: S;
declare let t: T;

// S is assignable to T0 when S["a"] is 0
// S is assignable to T2 when S["a"] is 2
t = s;
}

// Unmatched discriminants
namespace Example3 {
type S = { a: 0 | 2, b: 4 };
type T = { a: 0, b: 1 | 4 } // T0
| { a: 1, b: 2 | 4 } // T1
| { a: 2, b: 3 }; // T2
declare let s: S;
declare let t: T;

// S is assignable to T0 when S["a"] is 0
// S is *not* assignable to T1 when S["b"] is 4
// S is *not* assignable to T2 when S["a"] is 2
t = s;
~
!!! error TS2322: Type 'S' is not assignable to type 'T'.
!!! error TS2322: Type 'S' is not assignable to type '{ a: 2; b: 3; }'.
!!! error TS2322: Types of property 'a' are incompatible.
!!! error TS2322: Type '0 | 2' is not assignable to type '2'.
!!! error TS2322: Type '0' is not assignable to type '2'.
}

// Unmatched non-discriminants
namespace Example4 {
type S = { a: 0 | 2, b: 4 };
type T = { a: 0, b: 1 | 4 } // T0
| { a: 1, b: 2 } // T1
| { a: 2, b: 3 | 4, c: string }; // T2
declare let s: S;
declare let t: T;

// S is assignable to T0 when S["a"] is 0
// S is *not* assignable to T2 when S["a"] is 2 as S is missing "c"
t = s;
~
!!! error TS2322: Type 'S' is not assignable to type 'T'.
!!! error TS2322: Property 'c' is missing in type 'S' but required in type '{ a: 2; b: 4 | 3; c: string; }'.
!!! related TS2728 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts:52:36: 'c' is declared here.
}

// Maximum discriminant combinations
namespace Example5 {
// NOTE: The maximum number of discriminant type combinations is currently 25.
// 3 discriminant properties with 3 types a piece
// is 27 possible combinations.
type N = 0 | 1 | 2;
type S = { a: N, b: N, c: N };
type T = { a: 0, b: N, c: N }
| { a: 1, b: N, c: N }
| { a: 2, b: N, c: N }
| { a: N, b: 0, c: N }
| { a: N, b: 1, c: N }
| { a: N, b: 2, c: N }
| { a: N, b: N, c: 0 }
| { a: N, b: N, c: 1 }
| { a: N, b: N, c: 2 };
declare let s: S;
declare let t: T;

// S *should* be assignable but the number of
// combinations is too complex.
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: Types of property 'c' are incompatible.
!!! error TS2322: Type '0 | 2 | 1' is not assignable to type '2'.
!!! error TS2322: Type '0' is not assignable to type '2'.
}

// https://github.com/Microsoft/TypeScript/issues/14865
namespace GH14865 {
type Style1 = {
type: "A";
data: string;
} | {
type: "B";
data: string;
};

type Style2 = {
type: "A" | "B";
data: string;
}

const a: Style2 = { type: "A", data: "whatevs" };
let b: Style1;
a.type; // "A" | "B"
b.type; // "A" | "B"
b = a; // should be assignable
}

// https://github.com/Microsoft/TypeScript/issues/30170
namespace GH30170 {
interface Blue {
color: 'blue'
}
interface Yellow {
color?: 'yellow',
}
function draw(val: Blue | Yellow) { }

function drawWithColor(currentColor: 'blue' | 'yellow' | undefined) {
return draw({ color: currentColor });
}
}

// https://github.com/Microsoft/TypeScript/issues/12052
namespace GH12052 {
interface ILinearAxis { type: "linear"; }

interface ICategoricalAxis { type: "categorical"; }

type IAxis = ILinearAxis | ICategoricalAxis;
type IAxisType = "linear" | "categorical";

function getAxisType(): IAxisType {
if (1 == 1) {
return "categorical";
} else {
return "linear";
}
}

const bad: IAxis = { type: getAxisType() };
const good: IAxis = { type: undefined };
good.type = getAxisType();
}

// https://github.com/Microsoft/TypeScript/issues/18421
namespace GH18421 {
interface ThingTypeOne {
type: 'one';
}

interface ThingTypeTwo {
type: 'two';
}

type ThingType = 'one' | 'two';

type Thing = ThingTypeOne | ThingTypeTwo;

function makeNewThing(thingType: ThingType): Thing {
return {
type: thingType
};
}
}

// https://github.com/Microsoft/TypeScript/issues/15907
namespace GH15907 {
type Action = { type: 'activate' } | { type: 'disactivate' };

function dispatchAction(action: Action): void {

}

const active = true;

dispatchAction({ type : (active? 'disactivate' : 'activate') });
}

// https://github.com/Microsoft/TypeScript/issues/20889
namespace GH20889 {
interface A1 {
type: "A1";
}
interface A2 {
type: "A2";
}
type AU = A1 | A2;

function foo(obj1: AU) {
const obj2: AU = {
type: obj1.type
};
}
}
Loading

0 comments on commit 2d8527f

Please sign in to comment.