Skip to content
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

Relate source types covered by a target discriminated union #30779

Merged
merged 1 commit into from Apr 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
Expand Up @@ -2317,4 +2317,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);
}
}
}
}
@@ -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
};
}
}