diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 073c0d46d7b4e..6d53bc34c54eb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23998,7 +23998,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!targetProperty) continue outer; if (sourceProperty === targetProperty) continue; // We compare the source property to the target in the context of a single discriminant type. - const related = propertyRelatedTo(source, target, sourceProperty, targetProperty, _ => combination[i], /*reportErrors*/ false, IntersectionState.None, /*skipOptional*/ strictNullChecks || relation === comparableRelation); + const related = propertyRelatedTo(source, target, sourceProperty, targetProperty, _ => combination[i], /*reportErrors*/ false, IntersectionState.None, /*skipOptional*/ strictNullChecks && !exactOptionalPropertyTypes || relation === comparableRelation); // If the target property could not be found, or if the properties were not related, // then this constituent is not a match. if (!related) { diff --git a/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=false).errors.txt b/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=false).errors.txt new file mode 100644 index 0000000000000..161935d042999 --- /dev/null +++ b/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=false).errors.txt @@ -0,0 +1,46 @@ +unionRelationshipCheckPasses2.ts(10,3): error TS2322: Type '{ type: "A"; value?: undefined; } | { type: "B"; value: string; }' is not assignable to type 'U'. + Type '{ type: "A"; value?: undefined; }' is not assignable to type 'U'. + Type '{ type: "A"; value?: undefined; }' is not assignable to type '{ type: "A"; value: null; }'. + Types of property 'value' are incompatible. + Type 'undefined' is not assignable to type 'null'. +unionRelationshipCheckPasses2.ts(21,5): error TS2322: Type '{ type: "A"; }' is not assignable to type 'U'. + Property 'value' is missing in type '{ type: "A"; }' but required in type '{ type: "A"; value: null; }'. + + +==== unionRelationshipCheckPasses2.ts (2 errors) ==== + // https://github.com/microsoft/TypeScript/issues/61678 + + export type U = { type: "A"; value: null } | { type: "B"; value: string }; + + function call(f: () => T): T { + return f(); + } + + export function functionCall(): U { + return call(() => { // error + ~~~~~~ +!!! error TS2322: Type '{ type: "A"; value?: undefined; } | { type: "B"; value: string; }' is not assignable to type 'U'. +!!! error TS2322: Type '{ type: "A"; value?: undefined; }' is not assignable to type 'U'. +!!! error TS2322: Type '{ type: "A"; value?: undefined; }' is not assignable to type '{ type: "A"; value: null; }'. +!!! error TS2322: Types of property 'value' are incompatible. +!!! error TS2322: Type 'undefined' is not assignable to type 'null'. + if (Math.random()) { + return { type: "A" }; + } + + return { type: "B", value: "test" }; + }); + } + + export function directReturn(): U { + if (Math.random()) { + return { type: "A" }; // error + ~~~~~~ +!!! error TS2322: Type '{ type: "A"; }' is not assignable to type 'U'. +!!! error TS2322: Property 'value' is missing in type '{ type: "A"; }' but required in type '{ type: "A"; value: null; }'. +!!! related TS2728 unionRelationshipCheckPasses2.ts:3:30: 'value' is declared here. + } + + return { type: "B", value: "test" }; + } + \ No newline at end of file diff --git a/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=false).symbols b/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=false).symbols new file mode 100644 index 0000000000000..bda1ade854973 --- /dev/null +++ b/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=false).symbols @@ -0,0 +1,64 @@ +//// [tests/cases/compiler/unionRelationshipCheckPasses2.ts] //// + +=== unionRelationshipCheckPasses2.ts === +// https://github.com/microsoft/TypeScript/issues/61678 + +export type U = { type: "A"; value: null } | { type: "B"; value: string }; +>U : Symbol(U, Decl(unionRelationshipCheckPasses2.ts, 0, 0)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 2, 17)) +>value : Symbol(value, Decl(unionRelationshipCheckPasses2.ts, 2, 28)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 2, 46)) +>value : Symbol(value, Decl(unionRelationshipCheckPasses2.ts, 2, 57)) + +function call(f: () => T): T { +>call : Symbol(call, Decl(unionRelationshipCheckPasses2.ts, 2, 74)) +>T : Symbol(T, Decl(unionRelationshipCheckPasses2.ts, 4, 14)) +>f : Symbol(f, Decl(unionRelationshipCheckPasses2.ts, 4, 17)) +>T : Symbol(T, Decl(unionRelationshipCheckPasses2.ts, 4, 14)) +>T : Symbol(T, Decl(unionRelationshipCheckPasses2.ts, 4, 14)) + + return f(); +>f : Symbol(f, Decl(unionRelationshipCheckPasses2.ts, 4, 17)) +} + +export function functionCall(): U { +>functionCall : Symbol(functionCall, Decl(unionRelationshipCheckPasses2.ts, 6, 1)) +>U : Symbol(U, Decl(unionRelationshipCheckPasses2.ts, 0, 0)) + + return call(() => { // error +>call : Symbol(call, Decl(unionRelationshipCheckPasses2.ts, 2, 74)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + return { type: "A" }; +>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 11, 14)) + } + + return { type: "B", value: "test" }; +>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 14, 12)) +>value : Symbol(value, Decl(unionRelationshipCheckPasses2.ts, 14, 23)) + + }); +} + +export function directReturn(): U { +>directReturn : Symbol(directReturn, Decl(unionRelationshipCheckPasses2.ts, 16, 1)) +>U : Symbol(U, Decl(unionRelationshipCheckPasses2.ts, 0, 0)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + return { type: "A" }; // error +>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 20, 12)) + } + + return { type: "B", value: "test" }; +>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 23, 10)) +>value : Symbol(value, Decl(unionRelationshipCheckPasses2.ts, 23, 21)) +} + diff --git a/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=false).types b/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=false).types new file mode 100644 index 0000000000000..082fa817bff32 --- /dev/null +++ b/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=false).types @@ -0,0 +1,112 @@ +//// [tests/cases/compiler/unionRelationshipCheckPasses2.ts] //// + +=== unionRelationshipCheckPasses2.ts === +// https://github.com/microsoft/TypeScript/issues/61678 + +export type U = { type: "A"; value: null } | { type: "B"; value: string }; +>U : U +> : ^ +>type : "A" +> : ^^^ +>value : null +> : ^^^^ +>type : "B" +> : ^^^ +>value : string +> : ^^^^^^ + +function call(f: () => T): T { +>call : (f: () => T) => T +> : ^ ^^ ^^ ^^^^^ +>f : () => T +> : ^^^^^^ + + return f(); +>f() : T +> : ^ +>f : () => T +> : ^^^^^^ +} + +export function functionCall(): U { +>functionCall : () => U +> : ^^^^^^ + + return call(() => { // error +>call(() => { // error if (Math.random()) { return { type: "A" }; } return { type: "B", value: "test" }; }) : { type: "A"; value?: undefined; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>call : (f: () => T) => T +> : ^ ^^ ^^ ^^^^^ +>() => { // error if (Math.random()) { return { type: "A" }; } return { type: "B", value: "test" }; } : () => { type: "A"; value?: undefined; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + if (Math.random()) { +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ + + return { type: "A" }; +>{ type: "A" } : { type: "A"; } +> : ^^^^^^^^^^^^^^ +>type : "A" +> : ^^^ +>"A" : "A" +> : ^^^ + } + + return { type: "B", value: "test" }; +>{ type: "B", value: "test" } : { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>type : "B" +> : ^^^ +>"B" : "B" +> : ^^^ +>value : string +> : ^^^^^^ +>"test" : "test" +> : ^^^^^^ + + }); +} + +export function directReturn(): U { +>directReturn : () => U +> : ^^^^^^ + + if (Math.random()) { +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ + + return { type: "A" }; // error +>{ type: "A" } : { type: "A"; } +> : ^^^^^^^^^^^^^^ +>type : "A" +> : ^^^ +>"A" : "A" +> : ^^^ + } + + return { type: "B", value: "test" }; +>{ type: "B", value: "test" } : { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>type : "B" +> : ^^^ +>"B" : "B" +> : ^^^ +>value : string +> : ^^^^^^ +>"test" : "test" +> : ^^^^^^ +} + diff --git a/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=true).errors.txt b/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=true).errors.txt new file mode 100644 index 0000000000000..f34db1f4606eb --- /dev/null +++ b/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=true).errors.txt @@ -0,0 +1,44 @@ +unionRelationshipCheckPasses2.ts(10,3): error TS2322: Type '{ type: "A"; value?: never; } | { type: "B"; value: string; }' is not assignable to type 'U'. + Type '{ type: "A"; value?: never; }' is not assignable to type 'U'. + Type '{ type: "A"; value?: never; }' is not assignable to type '{ type: "A"; value: null; }'. + Property 'value' is optional in type '{ type: "A"; value?: never; }' but required in type '{ type: "A"; value: null; }'. +unionRelationshipCheckPasses2.ts(21,5): error TS2322: Type '{ type: "A"; }' is not assignable to type 'U'. + Property 'value' is missing in type '{ type: "A"; }' but required in type '{ type: "A"; value: null; }'. + + +==== unionRelationshipCheckPasses2.ts (2 errors) ==== + // https://github.com/microsoft/TypeScript/issues/61678 + + export type U = { type: "A"; value: null } | { type: "B"; value: string }; + + function call(f: () => T): T { + return f(); + } + + export function functionCall(): U { + return call(() => { // error + ~~~~~~ +!!! error TS2322: Type '{ type: "A"; value?: never; } | { type: "B"; value: string; }' is not assignable to type 'U'. +!!! error TS2322: Type '{ type: "A"; value?: never; }' is not assignable to type 'U'. +!!! error TS2322: Type '{ type: "A"; value?: never; }' is not assignable to type '{ type: "A"; value: null; }'. +!!! error TS2322: Property 'value' is optional in type '{ type: "A"; value?: never; }' but required in type '{ type: "A"; value: null; }'. + if (Math.random()) { + return { type: "A" }; + } + + return { type: "B", value: "test" }; + }); + } + + export function directReturn(): U { + if (Math.random()) { + return { type: "A" }; // error + ~~~~~~ +!!! error TS2322: Type '{ type: "A"; }' is not assignable to type 'U'. +!!! error TS2322: Property 'value' is missing in type '{ type: "A"; }' but required in type '{ type: "A"; value: null; }'. +!!! related TS2728 unionRelationshipCheckPasses2.ts:3:30: 'value' is declared here. + } + + return { type: "B", value: "test" }; + } + \ No newline at end of file diff --git a/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=true).symbols b/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=true).symbols new file mode 100644 index 0000000000000..bda1ade854973 --- /dev/null +++ b/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=true).symbols @@ -0,0 +1,64 @@ +//// [tests/cases/compiler/unionRelationshipCheckPasses2.ts] //// + +=== unionRelationshipCheckPasses2.ts === +// https://github.com/microsoft/TypeScript/issues/61678 + +export type U = { type: "A"; value: null } | { type: "B"; value: string }; +>U : Symbol(U, Decl(unionRelationshipCheckPasses2.ts, 0, 0)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 2, 17)) +>value : Symbol(value, Decl(unionRelationshipCheckPasses2.ts, 2, 28)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 2, 46)) +>value : Symbol(value, Decl(unionRelationshipCheckPasses2.ts, 2, 57)) + +function call(f: () => T): T { +>call : Symbol(call, Decl(unionRelationshipCheckPasses2.ts, 2, 74)) +>T : Symbol(T, Decl(unionRelationshipCheckPasses2.ts, 4, 14)) +>f : Symbol(f, Decl(unionRelationshipCheckPasses2.ts, 4, 17)) +>T : Symbol(T, Decl(unionRelationshipCheckPasses2.ts, 4, 14)) +>T : Symbol(T, Decl(unionRelationshipCheckPasses2.ts, 4, 14)) + + return f(); +>f : Symbol(f, Decl(unionRelationshipCheckPasses2.ts, 4, 17)) +} + +export function functionCall(): U { +>functionCall : Symbol(functionCall, Decl(unionRelationshipCheckPasses2.ts, 6, 1)) +>U : Symbol(U, Decl(unionRelationshipCheckPasses2.ts, 0, 0)) + + return call(() => { // error +>call : Symbol(call, Decl(unionRelationshipCheckPasses2.ts, 2, 74)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + return { type: "A" }; +>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 11, 14)) + } + + return { type: "B", value: "test" }; +>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 14, 12)) +>value : Symbol(value, Decl(unionRelationshipCheckPasses2.ts, 14, 23)) + + }); +} + +export function directReturn(): U { +>directReturn : Symbol(directReturn, Decl(unionRelationshipCheckPasses2.ts, 16, 1)) +>U : Symbol(U, Decl(unionRelationshipCheckPasses2.ts, 0, 0)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + return { type: "A" }; // error +>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 20, 12)) + } + + return { type: "B", value: "test" }; +>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 23, 10)) +>value : Symbol(value, Decl(unionRelationshipCheckPasses2.ts, 23, 21)) +} + diff --git a/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=true).types b/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=true).types new file mode 100644 index 0000000000000..51b017260b833 --- /dev/null +++ b/tests/baselines/reference/unionRelationshipCheckPasses2(exactoptionalpropertytypes=true).types @@ -0,0 +1,112 @@ +//// [tests/cases/compiler/unionRelationshipCheckPasses2.ts] //// + +=== unionRelationshipCheckPasses2.ts === +// https://github.com/microsoft/TypeScript/issues/61678 + +export type U = { type: "A"; value: null } | { type: "B"; value: string }; +>U : U +> : ^ +>type : "A" +> : ^^^ +>value : null +> : ^^^^ +>type : "B" +> : ^^^ +>value : string +> : ^^^^^^ + +function call(f: () => T): T { +>call : (f: () => T) => T +> : ^ ^^ ^^ ^^^^^ +>f : () => T +> : ^^^^^^ + + return f(); +>f() : T +> : ^ +>f : () => T +> : ^^^^^^ +} + +export function functionCall(): U { +>functionCall : () => U +> : ^^^^^^ + + return call(() => { // error +>call(() => { // error if (Math.random()) { return { type: "A" }; } return { type: "B", value: "test" }; }) : { type: "A"; value?: never; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>call : (f: () => T) => T +> : ^ ^^ ^^ ^^^^^ +>() => { // error if (Math.random()) { return { type: "A" }; } return { type: "B", value: "test" }; } : () => { type: "A"; value?: never; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + if (Math.random()) { +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ + + return { type: "A" }; +>{ type: "A" } : { type: "A"; } +> : ^^^^^^^^^^^^^^ +>type : "A" +> : ^^^ +>"A" : "A" +> : ^^^ + } + + return { type: "B", value: "test" }; +>{ type: "B", value: "test" } : { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>type : "B" +> : ^^^ +>"B" : "B" +> : ^^^ +>value : string +> : ^^^^^^ +>"test" : "test" +> : ^^^^^^ + + }); +} + +export function directReturn(): U { +>directReturn : () => U +> : ^^^^^^ + + if (Math.random()) { +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ + + return { type: "A" }; // error +>{ type: "A" } : { type: "A"; } +> : ^^^^^^^^^^^^^^ +>type : "A" +> : ^^^ +>"A" : "A" +> : ^^^ + } + + return { type: "B", value: "test" }; +>{ type: "B", value: "test" } : { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>type : "B" +> : ^^^ +>"B" : "B" +> : ^^^ +>value : string +> : ^^^^^^ +>"test" : "test" +> : ^^^^^^ +} + diff --git a/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=false).errors.txt b/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=false).errors.txt new file mode 100644 index 0000000000000..050f4b603761c --- /dev/null +++ b/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=false).errors.txt @@ -0,0 +1,56 @@ +unionRelationshipCheckPasses3.ts(5,7): error TS2322: Type '{ type: "A"; } | { type: "B"; value: string; }' is not assignable to type 'U1'. + Type '{ type: "A"; }' is not assignable to type 'U1'. + Property 'value' is missing in type '{ type: "A"; }' but required in type '{ type: "A"; value: 123; }'. +unionRelationshipCheckPasses3.ts(8,7): error TS2322: Type '{ type: "A"; value?: undefined; } | { type: "B"; value: string; }' is not assignable to type 'U1'. + Type '{ type: "A"; value?: undefined; }' is not assignable to type 'U1'. + Type '{ type: "A"; value?: undefined; }' is not assignable to type '{ type: "A"; value: 123; }'. + Types of property 'value' are incompatible. + Type 'undefined' is not assignable to type '123'. +unionRelationshipCheckPasses3.ts(12,7): error TS2322: Type '{ type: "A"; } | { type: "B"; value: string; }' is not assignable to type 'U2'. + Type '{ type: "A"; }' is not assignable to type 'U2'. + Property 'value' is missing in type '{ type: "A"; }' but required in type '{ type: "A"; value: number; }'. +unionRelationshipCheckPasses3.ts(15,7): error TS2322: Type '{ type: "A"; value?: undefined; } | { type: "B"; value: string; }' is not assignable to type 'U2'. + Type '{ type: "A"; value?: undefined; }' is not assignable to type 'U2'. + Type '{ type: "A"; value?: undefined; }' is not assignable to type '{ type: "A"; value: number; }'. + Types of property 'value' are incompatible. + Type 'undefined' is not assignable to type 'number'. + + +==== unionRelationshipCheckPasses3.ts (4 errors) ==== + // https://github.com/microsoft/TypeScript/issues/61678 + + export type U1 = { type: "A"; value: 123 } | { type: "B"; value: string }; + + const directAssignment1: U1 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; // error + ~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ type: "A"; } | { type: "B"; value: string; }' is not assignable to type 'U1'. +!!! error TS2322: Type '{ type: "A"; }' is not assignable to type 'U1'. +!!! error TS2322: Property 'value' is missing in type '{ type: "A"; }' but required in type '{ type: "A"; value: 123; }'. +!!! related TS2728 unionRelationshipCheckPasses3.ts:3:31: 'value' is declared here. + + const indirect1 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; + const indirectAssignment1: U1 = indirect1; // error + ~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ type: "A"; value?: undefined; } | { type: "B"; value: string; }' is not assignable to type 'U1'. +!!! error TS2322: Type '{ type: "A"; value?: undefined; }' is not assignable to type 'U1'. +!!! error TS2322: Type '{ type: "A"; value?: undefined; }' is not assignable to type '{ type: "A"; value: 123; }'. +!!! error TS2322: Types of property 'value' are incompatible. +!!! error TS2322: Type 'undefined' is not assignable to type '123'. + + export type U2 = { type: "A"; value: number } | { type: "B"; value: string }; + + const directAssignment2: U2 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; // error + ~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ type: "A"; } | { type: "B"; value: string; }' is not assignable to type 'U2'. +!!! error TS2322: Type '{ type: "A"; }' is not assignable to type 'U2'. +!!! error TS2322: Property 'value' is missing in type '{ type: "A"; }' but required in type '{ type: "A"; value: number; }'. +!!! related TS2728 unionRelationshipCheckPasses3.ts:10:31: 'value' is declared here. + + const indirect2 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; + const indirectAssignment2: U2 = indirect1; // error + ~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ type: "A"; value?: undefined; } | { type: "B"; value: string; }' is not assignable to type 'U2'. +!!! error TS2322: Type '{ type: "A"; value?: undefined; }' is not assignable to type 'U2'. +!!! error TS2322: Type '{ type: "A"; value?: undefined; }' is not assignable to type '{ type: "A"; value: number; }'. +!!! error TS2322: Types of property 'value' are incompatible. +!!! error TS2322: Type 'undefined' is not assignable to type 'number'. \ No newline at end of file diff --git a/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=false).symbols b/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=false).symbols new file mode 100644 index 0000000000000..7e063fbabff93 --- /dev/null +++ b/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=false).symbols @@ -0,0 +1,75 @@ +//// [tests/cases/compiler/unionRelationshipCheckPasses3.ts] //// + +=== unionRelationshipCheckPasses3.ts === +// https://github.com/microsoft/TypeScript/issues/61678 + +export type U1 = { type: "A"; value: 123 } | { type: "B"; value: string }; +>U1 : Symbol(U1, Decl(unionRelationshipCheckPasses3.ts, 0, 0)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 2, 18)) +>value : Symbol(value, Decl(unionRelationshipCheckPasses3.ts, 2, 29)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 2, 46)) +>value : Symbol(value, Decl(unionRelationshipCheckPasses3.ts, 2, 57)) + +const directAssignment1: U1 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; // error +>directAssignment1 : Symbol(directAssignment1, Decl(unionRelationshipCheckPasses3.ts, 4, 5)) +>U1 : Symbol(U1, Decl(unionRelationshipCheckPasses3.ts, 0, 0)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 4, 47)) +>const : Symbol(const) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 4, 72)) +>const : Symbol(const) +>value : Symbol(value, Decl(unionRelationshipCheckPasses3.ts, 4, 92)) + +const indirect1 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; +>indirect1 : Symbol(indirect1, Decl(unionRelationshipCheckPasses3.ts, 6, 5)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 6, 35)) +>const : Symbol(const) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 6, 60)) +>const : Symbol(const) +>value : Symbol(value, Decl(unionRelationshipCheckPasses3.ts, 6, 80)) + +const indirectAssignment1: U1 = indirect1; // error +>indirectAssignment1 : Symbol(indirectAssignment1, Decl(unionRelationshipCheckPasses3.ts, 7, 5)) +>U1 : Symbol(U1, Decl(unionRelationshipCheckPasses3.ts, 0, 0)) +>indirect1 : Symbol(indirect1, Decl(unionRelationshipCheckPasses3.ts, 6, 5)) + +export type U2 = { type: "A"; value: number } | { type: "B"; value: string }; +>U2 : Symbol(U2, Decl(unionRelationshipCheckPasses3.ts, 7, 42)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 9, 18)) +>value : Symbol(value, Decl(unionRelationshipCheckPasses3.ts, 9, 29)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 9, 49)) +>value : Symbol(value, Decl(unionRelationshipCheckPasses3.ts, 9, 60)) + +const directAssignment2: U2 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; // error +>directAssignment2 : Symbol(directAssignment2, Decl(unionRelationshipCheckPasses3.ts, 11, 5)) +>U2 : Symbol(U2, Decl(unionRelationshipCheckPasses3.ts, 7, 42)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 11, 47)) +>const : Symbol(const) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 11, 72)) +>const : Symbol(const) +>value : Symbol(value, Decl(unionRelationshipCheckPasses3.ts, 11, 92)) + +const indirect2 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; +>indirect2 : Symbol(indirect2, Decl(unionRelationshipCheckPasses3.ts, 13, 5)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 13, 35)) +>const : Symbol(const) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 13, 60)) +>const : Symbol(const) +>value : Symbol(value, Decl(unionRelationshipCheckPasses3.ts, 13, 80)) + +const indirectAssignment2: U2 = indirect1; // error +>indirectAssignment2 : Symbol(indirectAssignment2, Decl(unionRelationshipCheckPasses3.ts, 14, 5)) +>U2 : Symbol(U2, Decl(unionRelationshipCheckPasses3.ts, 7, 42)) +>indirect1 : Symbol(indirect1, Decl(unionRelationshipCheckPasses3.ts, 6, 5)) + diff --git a/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=false).types b/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=false).types new file mode 100644 index 0000000000000..15261f779790f --- /dev/null +++ b/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=false).types @@ -0,0 +1,177 @@ +//// [tests/cases/compiler/unionRelationshipCheckPasses3.ts] //// + +=== unionRelationshipCheckPasses3.ts === +// https://github.com/microsoft/TypeScript/issues/61678 + +export type U1 = { type: "A"; value: 123 } | { type: "B"; value: string }; +>U1 : U1 +> : ^^ +>type : "A" +> : ^^^ +>value : 123 +> : ^^^ +>type : "B" +> : ^^^ +>value : string +> : ^^^^^^ + +const directAssignment1: U1 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; // error +>directAssignment1 : U1 +> : ^^ +>Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" } : { type: "A"; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ +>{ type: "A" as const } : { type: "A"; } +> : ^^^^^^^^^^^^^^ +>type : "A" +> : ^^^ +>"A" as const : "A" +> : ^^^ +>"A" : "A" +> : ^^^ +>{ type: "B" as const, value: "test" } : { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>type : "B" +> : ^^^ +>"B" as const : "B" +> : ^^^ +>"B" : "B" +> : ^^^ +>value : string +> : ^^^^^^ +>"test" : "test" +> : ^^^^^^ + +const indirect1 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; +>indirect1 : { type: "A"; value?: undefined; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" } : { type: "A"; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ +>{ type: "A" as const } : { type: "A"; } +> : ^^^^^^^^^^^^^^ +>type : "A" +> : ^^^ +>"A" as const : "A" +> : ^^^ +>"A" : "A" +> : ^^^ +>{ type: "B" as const, value: "test" } : { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>type : "B" +> : ^^^ +>"B" as const : "B" +> : ^^^ +>"B" : "B" +> : ^^^ +>value : string +> : ^^^^^^ +>"test" : "test" +> : ^^^^^^ + +const indirectAssignment1: U1 = indirect1; // error +>indirectAssignment1 : U1 +> : ^^ +>indirect1 : { type: "A"; value?: undefined; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +export type U2 = { type: "A"; value: number } | { type: "B"; value: string }; +>U2 : U2 +> : ^^ +>type : "A" +> : ^^^ +>value : number +> : ^^^^^^ +>type : "B" +> : ^^^ +>value : string +> : ^^^^^^ + +const directAssignment2: U2 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; // error +>directAssignment2 : U2 +> : ^^ +>Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" } : { type: "A"; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ +>{ type: "A" as const } : { type: "A"; } +> : ^^^^^^^^^^^^^^ +>type : "A" +> : ^^^ +>"A" as const : "A" +> : ^^^ +>"A" : "A" +> : ^^^ +>{ type: "B" as const, value: "test" } : { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>type : "B" +> : ^^^ +>"B" as const : "B" +> : ^^^ +>"B" : "B" +> : ^^^ +>value : string +> : ^^^^^^ +>"test" : "test" +> : ^^^^^^ + +const indirect2 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; +>indirect2 : { type: "A"; value?: undefined; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" } : { type: "A"; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ +>{ type: "A" as const } : { type: "A"; } +> : ^^^^^^^^^^^^^^ +>type : "A" +> : ^^^ +>"A" as const : "A" +> : ^^^ +>"A" : "A" +> : ^^^ +>{ type: "B" as const, value: "test" } : { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>type : "B" +> : ^^^ +>"B" as const : "B" +> : ^^^ +>"B" : "B" +> : ^^^ +>value : string +> : ^^^^^^ +>"test" : "test" +> : ^^^^^^ + +const indirectAssignment2: U2 = indirect1; // error +>indirectAssignment2 : U2 +> : ^^ +>indirect1 : { type: "A"; value?: undefined; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + diff --git a/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=true).errors.txt b/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=true).errors.txt new file mode 100644 index 0000000000000..934c01e5a210f --- /dev/null +++ b/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=true).errors.txt @@ -0,0 +1,52 @@ +unionRelationshipCheckPasses3.ts(5,7): error TS2322: Type '{ type: "A"; } | { type: "B"; value: string; }' is not assignable to type 'U1'. + Type '{ type: "A"; }' is not assignable to type 'U1'. + Property 'value' is missing in type '{ type: "A"; }' but required in type '{ type: "A"; value: 123; }'. +unionRelationshipCheckPasses3.ts(8,7): error TS2322: Type '{ type: "A"; value?: never; } | { type: "B"; value: string; }' is not assignable to type 'U1'. + Type '{ type: "A"; value?: never; }' is not assignable to type 'U1'. + Type '{ type: "A"; value?: never; }' is not assignable to type '{ type: "A"; value: 123; }'. + Property 'value' is optional in type '{ type: "A"; value?: never; }' but required in type '{ type: "A"; value: 123; }'. +unionRelationshipCheckPasses3.ts(12,7): error TS2322: Type '{ type: "A"; } | { type: "B"; value: string; }' is not assignable to type 'U2'. + Type '{ type: "A"; }' is not assignable to type 'U2'. + Property 'value' is missing in type '{ type: "A"; }' but required in type '{ type: "A"; value: number; }'. +unionRelationshipCheckPasses3.ts(15,7): error TS2322: Type '{ type: "A"; value?: never; } | { type: "B"; value: string; }' is not assignable to type 'U2'. + Type '{ type: "A"; value?: never; }' is not assignable to type 'U2'. + Type '{ type: "A"; value?: never; }' is not assignable to type '{ type: "A"; value: number; }'. + Property 'value' is optional in type '{ type: "A"; value?: never; }' but required in type '{ type: "A"; value: number; }'. + + +==== unionRelationshipCheckPasses3.ts (4 errors) ==== + // https://github.com/microsoft/TypeScript/issues/61678 + + export type U1 = { type: "A"; value: 123 } | { type: "B"; value: string }; + + const directAssignment1: U1 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; // error + ~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ type: "A"; } | { type: "B"; value: string; }' is not assignable to type 'U1'. +!!! error TS2322: Type '{ type: "A"; }' is not assignable to type 'U1'. +!!! error TS2322: Property 'value' is missing in type '{ type: "A"; }' but required in type '{ type: "A"; value: 123; }'. +!!! related TS2728 unionRelationshipCheckPasses3.ts:3:31: 'value' is declared here. + + const indirect1 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; + const indirectAssignment1: U1 = indirect1; // error + ~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ type: "A"; value?: never; } | { type: "B"; value: string; }' is not assignable to type 'U1'. +!!! error TS2322: Type '{ type: "A"; value?: never; }' is not assignable to type 'U1'. +!!! error TS2322: Type '{ type: "A"; value?: never; }' is not assignable to type '{ type: "A"; value: 123; }'. +!!! error TS2322: Property 'value' is optional in type '{ type: "A"; value?: never; }' but required in type '{ type: "A"; value: 123; }'. + + export type U2 = { type: "A"; value: number } | { type: "B"; value: string }; + + const directAssignment2: U2 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; // error + ~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ type: "A"; } | { type: "B"; value: string; }' is not assignable to type 'U2'. +!!! error TS2322: Type '{ type: "A"; }' is not assignable to type 'U2'. +!!! error TS2322: Property 'value' is missing in type '{ type: "A"; }' but required in type '{ type: "A"; value: number; }'. +!!! related TS2728 unionRelationshipCheckPasses3.ts:10:31: 'value' is declared here. + + const indirect2 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; + const indirectAssignment2: U2 = indirect1; // error + ~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ type: "A"; value?: never; } | { type: "B"; value: string; }' is not assignable to type 'U2'. +!!! error TS2322: Type '{ type: "A"; value?: never; }' is not assignable to type 'U2'. +!!! error TS2322: Type '{ type: "A"; value?: never; }' is not assignable to type '{ type: "A"; value: number; }'. +!!! error TS2322: Property 'value' is optional in type '{ type: "A"; value?: never; }' but required in type '{ type: "A"; value: number; }'. \ No newline at end of file diff --git a/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=true).symbols b/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=true).symbols new file mode 100644 index 0000000000000..7e063fbabff93 --- /dev/null +++ b/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=true).symbols @@ -0,0 +1,75 @@ +//// [tests/cases/compiler/unionRelationshipCheckPasses3.ts] //// + +=== unionRelationshipCheckPasses3.ts === +// https://github.com/microsoft/TypeScript/issues/61678 + +export type U1 = { type: "A"; value: 123 } | { type: "B"; value: string }; +>U1 : Symbol(U1, Decl(unionRelationshipCheckPasses3.ts, 0, 0)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 2, 18)) +>value : Symbol(value, Decl(unionRelationshipCheckPasses3.ts, 2, 29)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 2, 46)) +>value : Symbol(value, Decl(unionRelationshipCheckPasses3.ts, 2, 57)) + +const directAssignment1: U1 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; // error +>directAssignment1 : Symbol(directAssignment1, Decl(unionRelationshipCheckPasses3.ts, 4, 5)) +>U1 : Symbol(U1, Decl(unionRelationshipCheckPasses3.ts, 0, 0)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 4, 47)) +>const : Symbol(const) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 4, 72)) +>const : Symbol(const) +>value : Symbol(value, Decl(unionRelationshipCheckPasses3.ts, 4, 92)) + +const indirect1 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; +>indirect1 : Symbol(indirect1, Decl(unionRelationshipCheckPasses3.ts, 6, 5)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 6, 35)) +>const : Symbol(const) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 6, 60)) +>const : Symbol(const) +>value : Symbol(value, Decl(unionRelationshipCheckPasses3.ts, 6, 80)) + +const indirectAssignment1: U1 = indirect1; // error +>indirectAssignment1 : Symbol(indirectAssignment1, Decl(unionRelationshipCheckPasses3.ts, 7, 5)) +>U1 : Symbol(U1, Decl(unionRelationshipCheckPasses3.ts, 0, 0)) +>indirect1 : Symbol(indirect1, Decl(unionRelationshipCheckPasses3.ts, 6, 5)) + +export type U2 = { type: "A"; value: number } | { type: "B"; value: string }; +>U2 : Symbol(U2, Decl(unionRelationshipCheckPasses3.ts, 7, 42)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 9, 18)) +>value : Symbol(value, Decl(unionRelationshipCheckPasses3.ts, 9, 29)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 9, 49)) +>value : Symbol(value, Decl(unionRelationshipCheckPasses3.ts, 9, 60)) + +const directAssignment2: U2 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; // error +>directAssignment2 : Symbol(directAssignment2, Decl(unionRelationshipCheckPasses3.ts, 11, 5)) +>U2 : Symbol(U2, Decl(unionRelationshipCheckPasses3.ts, 7, 42)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 11, 47)) +>const : Symbol(const) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 11, 72)) +>const : Symbol(const) +>value : Symbol(value, Decl(unionRelationshipCheckPasses3.ts, 11, 92)) + +const indirect2 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; +>indirect2 : Symbol(indirect2, Decl(unionRelationshipCheckPasses3.ts, 13, 5)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 13, 35)) +>const : Symbol(const) +>type : Symbol(type, Decl(unionRelationshipCheckPasses3.ts, 13, 60)) +>const : Symbol(const) +>value : Symbol(value, Decl(unionRelationshipCheckPasses3.ts, 13, 80)) + +const indirectAssignment2: U2 = indirect1; // error +>indirectAssignment2 : Symbol(indirectAssignment2, Decl(unionRelationshipCheckPasses3.ts, 14, 5)) +>U2 : Symbol(U2, Decl(unionRelationshipCheckPasses3.ts, 7, 42)) +>indirect1 : Symbol(indirect1, Decl(unionRelationshipCheckPasses3.ts, 6, 5)) + diff --git a/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=true).types b/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=true).types new file mode 100644 index 0000000000000..a52511bc68567 --- /dev/null +++ b/tests/baselines/reference/unionRelationshipCheckPasses3(exactoptionalpropertytypes=true).types @@ -0,0 +1,177 @@ +//// [tests/cases/compiler/unionRelationshipCheckPasses3.ts] //// + +=== unionRelationshipCheckPasses3.ts === +// https://github.com/microsoft/TypeScript/issues/61678 + +export type U1 = { type: "A"; value: 123 } | { type: "B"; value: string }; +>U1 : U1 +> : ^^ +>type : "A" +> : ^^^ +>value : 123 +> : ^^^ +>type : "B" +> : ^^^ +>value : string +> : ^^^^^^ + +const directAssignment1: U1 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; // error +>directAssignment1 : U1 +> : ^^ +>Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" } : { type: "A"; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ +>{ type: "A" as const } : { type: "A"; } +> : ^^^^^^^^^^^^^^ +>type : "A" +> : ^^^ +>"A" as const : "A" +> : ^^^ +>"A" : "A" +> : ^^^ +>{ type: "B" as const, value: "test" } : { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>type : "B" +> : ^^^ +>"B" as const : "B" +> : ^^^ +>"B" : "B" +> : ^^^ +>value : string +> : ^^^^^^ +>"test" : "test" +> : ^^^^^^ + +const indirect1 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; +>indirect1 : { type: "A"; value?: never; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" } : { type: "A"; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ +>{ type: "A" as const } : { type: "A"; } +> : ^^^^^^^^^^^^^^ +>type : "A" +> : ^^^ +>"A" as const : "A" +> : ^^^ +>"A" : "A" +> : ^^^ +>{ type: "B" as const, value: "test" } : { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>type : "B" +> : ^^^ +>"B" as const : "B" +> : ^^^ +>"B" : "B" +> : ^^^ +>value : string +> : ^^^^^^ +>"test" : "test" +> : ^^^^^^ + +const indirectAssignment1: U1 = indirect1; // error +>indirectAssignment1 : U1 +> : ^^ +>indirect1 : { type: "A"; value?: never; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +export type U2 = { type: "A"; value: number } | { type: "B"; value: string }; +>U2 : U2 +> : ^^ +>type : "A" +> : ^^^ +>value : number +> : ^^^^^^ +>type : "B" +> : ^^^ +>value : string +> : ^^^^^^ + +const directAssignment2: U2 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; // error +>directAssignment2 : U2 +> : ^^ +>Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" } : { type: "A"; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ +>{ type: "A" as const } : { type: "A"; } +> : ^^^^^^^^^^^^^^ +>type : "A" +> : ^^^ +>"A" as const : "A" +> : ^^^ +>"A" : "A" +> : ^^^ +>{ type: "B" as const, value: "test" } : { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>type : "B" +> : ^^^ +>"B" as const : "B" +> : ^^^ +>"B" : "B" +> : ^^^ +>value : string +> : ^^^^^^ +>"test" : "test" +> : ^^^^^^ + +const indirect2 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; +>indirect2 : { type: "A"; value?: never; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" } : { type: "A"; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ +>{ type: "A" as const } : { type: "A"; } +> : ^^^^^^^^^^^^^^ +>type : "A" +> : ^^^ +>"A" as const : "A" +> : ^^^ +>"A" : "A" +> : ^^^ +>{ type: "B" as const, value: "test" } : { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>type : "B" +> : ^^^ +>"B" as const : "B" +> : ^^^ +>"B" : "B" +> : ^^^ +>value : string +> : ^^^^^^ +>"test" : "test" +> : ^^^^^^ + +const indirectAssignment2: U2 = indirect1; // error +>indirectAssignment2 : U2 +> : ^^ +>indirect1 : { type: "A"; value?: never; } | { type: "B"; value: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + diff --git a/tests/cases/compiler/unionRelationshipCheckPasses2.ts b/tests/cases/compiler/unionRelationshipCheckPasses2.ts new file mode 100644 index 0000000000000..26fb718e64073 --- /dev/null +++ b/tests/cases/compiler/unionRelationshipCheckPasses2.ts @@ -0,0 +1,29 @@ +// @strict: true +// @noEmit: true +// @exactOptionalPropertyTypes: true, false + +// https://github.com/microsoft/TypeScript/issues/61678 + +export type U = { type: "A"; value: null } | { type: "B"; value: string }; + +function call(f: () => T): T { + return f(); +} + +export function functionCall(): U { + return call(() => { // error + if (Math.random()) { + return { type: "A" }; + } + + return { type: "B", value: "test" }; + }); +} + +export function directReturn(): U { + if (Math.random()) { + return { type: "A" }; // error + } + + return { type: "B", value: "test" }; +} diff --git a/tests/cases/compiler/unionRelationshipCheckPasses3.ts b/tests/cases/compiler/unionRelationshipCheckPasses3.ts new file mode 100644 index 0000000000000..e6358257fd021 --- /dev/null +++ b/tests/cases/compiler/unionRelationshipCheckPasses3.ts @@ -0,0 +1,19 @@ +// @strict: true +// @noEmit: true +// @exactOptionalPropertyTypes: true, false + +// https://github.com/microsoft/TypeScript/issues/61678 + +export type U1 = { type: "A"; value: 123 } | { type: "B"; value: string }; + +const directAssignment1: U1 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; // error + +const indirect1 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; +const indirectAssignment1: U1 = indirect1; // error + +export type U2 = { type: "A"; value: number } | { type: "B"; value: string }; + +const directAssignment2: U2 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; // error + +const indirect2 = Math.random() ? { type: "A" as const } : { type: "B" as const, value: "test" }; +const indirectAssignment2: U2 = indirect1; // error \ No newline at end of file