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

Reduce away intersections that aren't assignable to constituents in CFA #37724

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12387,6 +12387,11 @@ namespace ts {
return links.resolvedType;
}

function getAssignableIntersectionType(type1: Type, type2: Type) {
const type = getIntersectionType([type1, type2]);
return isTypeAssignableTo(type, type1) && isTypeAssignableTo(type, type2) ? type : neverType;
}

function createIndexType(type: InstantiableType | UnionOrIntersectionType, stringsOnly: boolean) {
const result = <IndexType>createType(TypeFlags.Index);
result.type = type;
Expand Down Expand Up @@ -20678,7 +20683,7 @@ namespace ts {
return isTypeSubtypeOf(candidate, type) ? candidate :
isTypeAssignableTo(type, candidate) ? type :
isTypeAssignableTo(candidate, type) ? candidate :
getIntersectionType([type, candidate]);
getAssignableIntersectionType(type, candidate);
}

function narrowTypeByCallExpression(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
tests/cases/compiler/intersectionNotAssignableToConstituents.ts(15,3): error TS2322: Type 'B' is not assignable to type 'A'.
Types have separate declarations of a private property 'x'.
tests/cases/compiler/intersectionNotAssignableToConstituents.ts(16,3): error TS2322: Type 'A' is not assignable to type 'B'.
Types have separate declarations of a private property 'x'.
tests/cases/compiler/intersectionNotAssignableToConstituents.ts(17,3): error TS2322: Type 'A & B' is not assignable to type 'A'.
Property 'x' has conflicting declarations and is inaccessible in type 'A & B'.
tests/cases/compiler/intersectionNotAssignableToConstituents.ts(18,3): error TS2322: Type 'A & B' is not assignable to type 'B'.
Property 'x' has conflicting declarations and is inaccessible in type 'A & B'.


==== tests/cases/compiler/intersectionNotAssignableToConstituents.ts (4 errors) ====
class A { private x: unknown }
class B { private x: unknown }

function f1(node: A | B) {
if (node instanceof A || node instanceof A) {
node; // A
}
else {
node; // B
}
node; // A | B
}

function f2(a: A, b: B, c: A & B) {
a = b; // Error
~
!!! error TS2322: Type 'B' is not assignable to type 'A'.
!!! error TS2322: Types have separate declarations of a private property 'x'.
b = a; // Error
~
!!! error TS2322: Type 'A' is not assignable to type 'B'.
!!! error TS2322: Types have separate declarations of a private property 'x'.
a = c; // Error (conflicting private fields)
~
!!! error TS2322: Type 'A & B' is not assignable to type 'A'.
!!! error TS2322: Property 'x' has conflicting declarations and is inaccessible in type 'A & B'.
b = c; // Error (conflicting private fields)
~
!!! error TS2322: Type 'A & B' is not assignable to type 'B'.
!!! error TS2322: Property 'x' has conflicting declarations and is inaccessible in type 'A & B'.
}

// Repro from #37659

abstract class ViewNode { }
abstract class ViewRefNode extends ViewNode { }
abstract class ViewRefFileNode extends ViewRefNode { }

class CommitFileNode extends ViewRefFileNode {
private _id: any;
}

class ResultsFileNode extends ViewRefFileNode {
private _id: any;
}

class StashFileNode extends CommitFileNode {
private _id2: any;
}

class StatusFileNode extends ViewNode {
private _id: any;
}

class Foo {
private async foo(node: CommitFileNode | ResultsFileNode | StashFileNode) {
if (
!(node instanceof CommitFileNode) &&
!(node instanceof StashFileNode) &&
!(node instanceof ResultsFileNode)
) {
return;
}

await this.bar(node);
}

private async bar(node: CommitFileNode | ResultsFileNode | StashFileNode | StatusFileNode, options?: {}) {
return Promise.resolve(undefined);
}
}

111 changes: 111 additions & 0 deletions tests/baselines/reference/intersectionNotAssignableToConstituents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//// [intersectionNotAssignableToConstituents.ts]
class A { private x: unknown }
class B { private x: unknown }

function f1(node: A | B) {
if (node instanceof A || node instanceof A) {
node; // A
}
else {
node; // B
}
node; // A | B
}

function f2(a: A, b: B, c: A & B) {
a = b; // Error
b = a; // Error
a = c; // Error (conflicting private fields)
b = c; // Error (conflicting private fields)
}

// Repro from #37659

abstract class ViewNode { }
abstract class ViewRefNode extends ViewNode { }
abstract class ViewRefFileNode extends ViewRefNode { }

class CommitFileNode extends ViewRefFileNode {
private _id: any;
}

class ResultsFileNode extends ViewRefFileNode {
private _id: any;
}

class StashFileNode extends CommitFileNode {
private _id2: any;
}

class StatusFileNode extends ViewNode {
private _id: any;
}

class Foo {
private async foo(node: CommitFileNode | ResultsFileNode | StashFileNode) {
if (
!(node instanceof CommitFileNode) &&
!(node instanceof StashFileNode) &&
!(node instanceof ResultsFileNode)
) {
return;
}

await this.bar(node);
}

private async bar(node: CommitFileNode | ResultsFileNode | StashFileNode | StatusFileNode, options?: {}) {
return Promise.resolve(undefined);
}
}


//// [intersectionNotAssignableToConstituents.js]
"use strict";
class A {
}
class B {
}
function f1(node) {
if (node instanceof A || node instanceof A) {
node; // A
}
else {
node; // B
}
node; // A | B
}
function f2(a, b, c) {
a = b; // Error
b = a; // Error
a = c; // Error (conflicting private fields)
b = c; // Error (conflicting private fields)
}
// Repro from #37659
class ViewNode {
}
class ViewRefNode extends ViewNode {
}
class ViewRefFileNode extends ViewRefNode {
}
class CommitFileNode extends ViewRefFileNode {
}
class ResultsFileNode extends ViewRefFileNode {
}
class StashFileNode extends CommitFileNode {
}
class StatusFileNode extends ViewNode {
}
class Foo {
async foo(node) {
if (!(node instanceof CommitFileNode) &&
!(node instanceof StashFileNode) &&
!(node instanceof ResultsFileNode)) {
return;
}
await this.bar(node);
}
async bar(node, options) {
return Promise.resolve(undefined);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
=== tests/cases/compiler/intersectionNotAssignableToConstituents.ts ===
class A { private x: unknown }
>A : Symbol(A, Decl(intersectionNotAssignableToConstituents.ts, 0, 0))
>x : Symbol(A.x, Decl(intersectionNotAssignableToConstituents.ts, 0, 9))

class B { private x: unknown }
>B : Symbol(B, Decl(intersectionNotAssignableToConstituents.ts, 0, 30))
>x : Symbol(B.x, Decl(intersectionNotAssignableToConstituents.ts, 1, 9))

function f1(node: A | B) {
>f1 : Symbol(f1, Decl(intersectionNotAssignableToConstituents.ts, 1, 30))
>node : Symbol(node, Decl(intersectionNotAssignableToConstituents.ts, 3, 12))
>A : Symbol(A, Decl(intersectionNotAssignableToConstituents.ts, 0, 0))
>B : Symbol(B, Decl(intersectionNotAssignableToConstituents.ts, 0, 30))

if (node instanceof A || node instanceof A) {
>node : Symbol(node, Decl(intersectionNotAssignableToConstituents.ts, 3, 12))
>A : Symbol(A, Decl(intersectionNotAssignableToConstituents.ts, 0, 0))
>node : Symbol(node, Decl(intersectionNotAssignableToConstituents.ts, 3, 12))
>A : Symbol(A, Decl(intersectionNotAssignableToConstituents.ts, 0, 0))

node; // A
>node : Symbol(node, Decl(intersectionNotAssignableToConstituents.ts, 3, 12))
}
else {
node; // B
>node : Symbol(node, Decl(intersectionNotAssignableToConstituents.ts, 3, 12))
}
node; // A | B
>node : Symbol(node, Decl(intersectionNotAssignableToConstituents.ts, 3, 12))
}

function f2(a: A, b: B, c: A & B) {
>f2 : Symbol(f2, Decl(intersectionNotAssignableToConstituents.ts, 11, 1))
>a : Symbol(a, Decl(intersectionNotAssignableToConstituents.ts, 13, 12))
>A : Symbol(A, Decl(intersectionNotAssignableToConstituents.ts, 0, 0))
>b : Symbol(b, Decl(intersectionNotAssignableToConstituents.ts, 13, 17))
>B : Symbol(B, Decl(intersectionNotAssignableToConstituents.ts, 0, 30))
>c : Symbol(c, Decl(intersectionNotAssignableToConstituents.ts, 13, 23))
>A : Symbol(A, Decl(intersectionNotAssignableToConstituents.ts, 0, 0))
>B : Symbol(B, Decl(intersectionNotAssignableToConstituents.ts, 0, 30))

a = b; // Error
>a : Symbol(a, Decl(intersectionNotAssignableToConstituents.ts, 13, 12))
>b : Symbol(b, Decl(intersectionNotAssignableToConstituents.ts, 13, 17))

b = a; // Error
>b : Symbol(b, Decl(intersectionNotAssignableToConstituents.ts, 13, 17))
>a : Symbol(a, Decl(intersectionNotAssignableToConstituents.ts, 13, 12))

a = c; // Error (conflicting private fields)
>a : Symbol(a, Decl(intersectionNotAssignableToConstituents.ts, 13, 12))
>c : Symbol(c, Decl(intersectionNotAssignableToConstituents.ts, 13, 23))

b = c; // Error (conflicting private fields)
>b : Symbol(b, Decl(intersectionNotAssignableToConstituents.ts, 13, 17))
>c : Symbol(c, Decl(intersectionNotAssignableToConstituents.ts, 13, 23))
}

// Repro from #37659

abstract class ViewNode { }
>ViewNode : Symbol(ViewNode, Decl(intersectionNotAssignableToConstituents.ts, 18, 1))

abstract class ViewRefNode extends ViewNode { }
>ViewRefNode : Symbol(ViewRefNode, Decl(intersectionNotAssignableToConstituents.ts, 22, 27))
>ViewNode : Symbol(ViewNode, Decl(intersectionNotAssignableToConstituents.ts, 18, 1))

abstract class ViewRefFileNode extends ViewRefNode { }
>ViewRefFileNode : Symbol(ViewRefFileNode, Decl(intersectionNotAssignableToConstituents.ts, 23, 47))
>ViewRefNode : Symbol(ViewRefNode, Decl(intersectionNotAssignableToConstituents.ts, 22, 27))

class CommitFileNode extends ViewRefFileNode {
>CommitFileNode : Symbol(CommitFileNode, Decl(intersectionNotAssignableToConstituents.ts, 24, 54))
>ViewRefFileNode : Symbol(ViewRefFileNode, Decl(intersectionNotAssignableToConstituents.ts, 23, 47))

private _id: any;
>_id : Symbol(CommitFileNode._id, Decl(intersectionNotAssignableToConstituents.ts, 26, 46))
}

class ResultsFileNode extends ViewRefFileNode {
>ResultsFileNode : Symbol(ResultsFileNode, Decl(intersectionNotAssignableToConstituents.ts, 28, 1))
>ViewRefFileNode : Symbol(ViewRefFileNode, Decl(intersectionNotAssignableToConstituents.ts, 23, 47))

private _id: any;
>_id : Symbol(ResultsFileNode._id, Decl(intersectionNotAssignableToConstituents.ts, 30, 47))
}

class StashFileNode extends CommitFileNode {
>StashFileNode : Symbol(StashFileNode, Decl(intersectionNotAssignableToConstituents.ts, 32, 1))
>CommitFileNode : Symbol(CommitFileNode, Decl(intersectionNotAssignableToConstituents.ts, 24, 54))

private _id2: any;
>_id2 : Symbol(StashFileNode._id2, Decl(intersectionNotAssignableToConstituents.ts, 34, 44))
}

class StatusFileNode extends ViewNode {
>StatusFileNode : Symbol(StatusFileNode, Decl(intersectionNotAssignableToConstituents.ts, 36, 1))
>ViewNode : Symbol(ViewNode, Decl(intersectionNotAssignableToConstituents.ts, 18, 1))

private _id: any;
>_id : Symbol(StatusFileNode._id, Decl(intersectionNotAssignableToConstituents.ts, 38, 39))
}

class Foo {
>Foo : Symbol(Foo, Decl(intersectionNotAssignableToConstituents.ts, 40, 1))

private async foo(node: CommitFileNode | ResultsFileNode | StashFileNode) {
>foo : Symbol(Foo.foo, Decl(intersectionNotAssignableToConstituents.ts, 42, 11))
>node : Symbol(node, Decl(intersectionNotAssignableToConstituents.ts, 43, 20))
>CommitFileNode : Symbol(CommitFileNode, Decl(intersectionNotAssignableToConstituents.ts, 24, 54))
>ResultsFileNode : Symbol(ResultsFileNode, Decl(intersectionNotAssignableToConstituents.ts, 28, 1))
>StashFileNode : Symbol(StashFileNode, Decl(intersectionNotAssignableToConstituents.ts, 32, 1))

if (
!(node instanceof CommitFileNode) &&
>node : Symbol(node, Decl(intersectionNotAssignableToConstituents.ts, 43, 20))
>CommitFileNode : Symbol(CommitFileNode, Decl(intersectionNotAssignableToConstituents.ts, 24, 54))

!(node instanceof StashFileNode) &&
>node : Symbol(node, Decl(intersectionNotAssignableToConstituents.ts, 43, 20))
>StashFileNode : Symbol(StashFileNode, Decl(intersectionNotAssignableToConstituents.ts, 32, 1))

!(node instanceof ResultsFileNode)
>node : Symbol(node, Decl(intersectionNotAssignableToConstituents.ts, 43, 20))
>ResultsFileNode : Symbol(ResultsFileNode, Decl(intersectionNotAssignableToConstituents.ts, 28, 1))

) {
return;
}

await this.bar(node);
>this.bar : Symbol(Foo.bar, Decl(intersectionNotAssignableToConstituents.ts, 53, 2))
>this : Symbol(Foo, Decl(intersectionNotAssignableToConstituents.ts, 40, 1))
>bar : Symbol(Foo.bar, Decl(intersectionNotAssignableToConstituents.ts, 53, 2))
>node : Symbol(node, Decl(intersectionNotAssignableToConstituents.ts, 43, 20))
}

private async bar(node: CommitFileNode | ResultsFileNode | StashFileNode | StatusFileNode, options?: {}) {
>bar : Symbol(Foo.bar, Decl(intersectionNotAssignableToConstituents.ts, 53, 2))
>node : Symbol(node, Decl(intersectionNotAssignableToConstituents.ts, 55, 20))
>CommitFileNode : Symbol(CommitFileNode, Decl(intersectionNotAssignableToConstituents.ts, 24, 54))
>ResultsFileNode : Symbol(ResultsFileNode, Decl(intersectionNotAssignableToConstituents.ts, 28, 1))
>StashFileNode : Symbol(StashFileNode, Decl(intersectionNotAssignableToConstituents.ts, 32, 1))
>StatusFileNode : Symbol(StatusFileNode, Decl(intersectionNotAssignableToConstituents.ts, 36, 1))
>options : Symbol(options, Decl(intersectionNotAssignableToConstituents.ts, 55, 92))

return Promise.resolve(undefined);
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
>undefined : Symbol(undefined)
}
}

Loading