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

Control Flow Type Inference: Inferred Type Varying by Condition Order #11426

Closed
ethanresnick opened this issue Oct 6, 2016 · 4 comments
Closed
Labels
Bug A bug in TypeScript

Comments

@ethanresnick
Copy link
Contributor

ethanresnick commented Oct 6, 2016

TypeScript Version: nightly (2.1.0-dev.20161006)

Code

type A_or_C_Child = A | C_Child;

interface A {
  a: string;
}

interface B {
  b: string;
}

interface C {
  c: string;
}

interface C_Child extends A, C {
  extra: string;
}

declare function isA(it: any): it is A;
declare function isB(it: any): it is B;
declare function isC(it: any): it is C;
declare function isA_or_C_Child(it: any): it is A_or_C_Child;

let item: any;
if (isA_or_C_Child(item) && !isC(item) && isB(item)) {
  item; // inferred as A & B
}

if (isA_or_C_Child(item) && isB(item) && !isC(item)) {
  item; // inferred as A_or_C_Child & B
}

Expected behavior:
Within the the last two if statements, item should be inferred to be the same type, as both if statements use the same type guards &&ed together.

Actual behavior:
item is (correctly, I think) inferred as A & B in the first if, but as A_or_C_Child & B in the second.

@ethanresnick
Copy link
Contributor Author

Any idea what's going on here? Can others reproduce?

@mhegazy
Copy link
Contributor

mhegazy commented Oct 10, 2016

The way narrowing works is by removing types from a union. in some cases, where there are two constraints that are not assignable, an intersection is created, e.g. isA(item) && isB(item).

the first clause isA_or_C_Child(item) narrows the type from any to A | C, !isC() removes the C from the union, then isB() adds the B.

the second one, isA_or_C_Child(item) narrows the type from any to A | C, then we add in the B because of isB(item). then the !isC(item) happens, and at this point we have (A | C ) & B, so trying to remove C from that results in no change.

I suppose this should be fixed by going deep on the intersection to see if we can remove one of the constituents. though it is not clear how this work in more complex situations.

@mhegazy mhegazy added the Bug A bug in TypeScript label Oct 10, 2016
@ethanresnick
Copy link
Contributor Author

@mhegazy Thanks for the explanation!

@ethanresnick
Copy link
Contributor Author

This is fixed by #11717

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug A bug in TypeScript
Projects
None yet
Development

No branches or pull requests

2 participants