Skip to content

Commit

Permalink
Allow intersections (and substitutions) to be checks against discrimi…
Browse files Browse the repository at this point in the history
…nable unions (#36663)
  • Loading branch information
weswigham committed Feb 10, 2020
1 parent 70e6f5b commit aece8c0
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 3 deletions.
6 changes: 3 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15901,7 +15901,7 @@ namespace ts {
// with respect to T. We do not report errors here, as we will use the existing
// error result from checking each constituent of the union.
if (source.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Union) {
const objectOnlyTarget = extractTypesOfKind(target, TypeFlags.Object);
const objectOnlyTarget = extractTypesOfKind(target, TypeFlags.Object | TypeFlags.Intersection | TypeFlags.Substitution);
if (objectOnlyTarget.flags & TypeFlags.Union) {
const result = typeRelatedToDiscriminatedType(source, objectOnlyTarget as UnionType);
if (result) {
Expand Down Expand Up @@ -15998,7 +15998,7 @@ namespace ts {
// NOTE: See ~/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts
// for examples.

const sourceProperties = getPropertiesOfObjectType(source);
const sourceProperties = getPropertiesOfType(source);
const sourcePropertiesFiltered = findDiscriminantProperties(sourceProperties, target);
if (!sourcePropertiesFiltered) return Ternary.False;

Expand Down Expand Up @@ -16037,7 +16037,7 @@ namespace ts {
outer: for (const type of target.types) {
for (let i = 0; i < sourcePropertiesFiltered.length; i++) {
const sourceProperty = sourcePropertiesFiltered[i];
const targetProperty = getPropertyOfObjectType(type, sourceProperty.escapedName);
const targetProperty = getPropertyOfType(type, sourceProperty.escapedName);
if (!targetProperty) continue outer;
if (sourceProperty === targetProperty) continue;
// We compare the source property to the target in the context of a single discriminant type.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//// [discriminableUnionWithIntersectedMembers.ts]
// regression test for https://github.com/microsoft/TypeScript/issues/33243
type X =
| { x: 'x', y: number } & { y: number }
| { x: 'y', y: number, z?: boolean } & { y: number }

// error
const x: X = 4 as any as { x: 'x' | 'y', y: number };

type Y =
| { x: 'x', y: number }
| { x: 'y', y: number, z?: boolean }

// no error
const y: Y = 4 as any as { x: 'x' | 'y', y: number };

//// [discriminableUnionWithIntersectedMembers.js]
// error
var x = 4;
// no error
var y = 4;
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
=== tests/cases/compiler/discriminableUnionWithIntersectedMembers.ts ===
// regression test for https://github.com/microsoft/TypeScript/issues/33243
type X =
>X : Symbol(X, Decl(discriminableUnionWithIntersectedMembers.ts, 0, 0))

| { x: 'x', y: number } & { y: number }
>x : Symbol(x, Decl(discriminableUnionWithIntersectedMembers.ts, 2, 4))
>y : Symbol(y, Decl(discriminableUnionWithIntersectedMembers.ts, 2, 12))
>y : Symbol(y, Decl(discriminableUnionWithIntersectedMembers.ts, 2, 28))

| { x: 'y', y: number, z?: boolean } & { y: number }
>x : Symbol(x, Decl(discriminableUnionWithIntersectedMembers.ts, 3, 4))
>y : Symbol(y, Decl(discriminableUnionWithIntersectedMembers.ts, 3, 12))
>z : Symbol(z, Decl(discriminableUnionWithIntersectedMembers.ts, 3, 23))
>y : Symbol(y, Decl(discriminableUnionWithIntersectedMembers.ts, 3, 41))

// error
const x: X = 4 as any as { x: 'x' | 'y', y: number };
>x : Symbol(x, Decl(discriminableUnionWithIntersectedMembers.ts, 6, 5))
>X : Symbol(X, Decl(discriminableUnionWithIntersectedMembers.ts, 0, 0))
>x : Symbol(x, Decl(discriminableUnionWithIntersectedMembers.ts, 6, 26))
>y : Symbol(y, Decl(discriminableUnionWithIntersectedMembers.ts, 6, 40))

type Y =
>Y : Symbol(Y, Decl(discriminableUnionWithIntersectedMembers.ts, 6, 53))

| { x: 'x', y: number }
>x : Symbol(x, Decl(discriminableUnionWithIntersectedMembers.ts, 9, 4))
>y : Symbol(y, Decl(discriminableUnionWithIntersectedMembers.ts, 9, 12))

| { x: 'y', y: number, z?: boolean }
>x : Symbol(x, Decl(discriminableUnionWithIntersectedMembers.ts, 10, 4))
>y : Symbol(y, Decl(discriminableUnionWithIntersectedMembers.ts, 10, 12))
>z : Symbol(z, Decl(discriminableUnionWithIntersectedMembers.ts, 10, 23))

// no error
const y: Y = 4 as any as { x: 'x' | 'y', y: number };
>y : Symbol(y, Decl(discriminableUnionWithIntersectedMembers.ts, 13, 5))
>Y : Symbol(Y, Decl(discriminableUnionWithIntersectedMembers.ts, 6, 53))
>x : Symbol(x, Decl(discriminableUnionWithIntersectedMembers.ts, 13, 26))
>y : Symbol(y, Decl(discriminableUnionWithIntersectedMembers.ts, 13, 40))

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
=== tests/cases/compiler/discriminableUnionWithIntersectedMembers.ts ===
// regression test for https://github.com/microsoft/TypeScript/issues/33243
type X =
>X : X

| { x: 'x', y: number } & { y: number }
>x : "x"
>y : number
>y : number

| { x: 'y', y: number, z?: boolean } & { y: number }
>x : "y"
>y : number
>z : boolean
>y : number

// error
const x: X = 4 as any as { x: 'x' | 'y', y: number };
>x : X
>4 as any as { x: 'x' | 'y', y: number } : { x: "x" | "y"; y: number; }
>4 as any : any
>4 : 4
>x : "x" | "y"
>y : number

type Y =
>Y : Y

| { x: 'x', y: number }
>x : "x"
>y : number

| { x: 'y', y: number, z?: boolean }
>x : "y"
>y : number
>z : boolean

// no error
const y: Y = 4 as any as { x: 'x' | 'y', y: number };
>y : Y
>4 as any as { x: 'x' | 'y', y: number } : { x: "x" | "y"; y: number; }
>4 as any : any
>4 : 4
>x : "x" | "y"
>y : number

14 changes: 14 additions & 0 deletions tests/cases/compiler/discriminableUnionWithIntersectedMembers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// regression test for https://github.com/microsoft/TypeScript/issues/33243
type X =
| { x: 'x', y: number } & { y: number }
| { x: 'y', y: number, z?: boolean } & { y: number }

// error
const x: X = 4 as any as { x: 'x' | 'y', y: number };

type Y =
| { x: 'x', y: number }
| { x: 'y', y: number, z?: boolean }

// no error
const y: Y = 4 as any as { x: 'x' | 'y', y: number };

0 comments on commit aece8c0

Please sign in to comment.