-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Using intersection with unions #8896
Comments
I think it's easier to read if you inline and shrink the types: let baz: ({ a } | { b }) & { c };
if (baz.a) {
let qux: { a } & { c } = baz;
} The problem is that, even with control flow analysis, the compiler still doesn't do much "type math". All control flow will do is narrow -- that is, throw out members of a union. What needs to happen here is for the compiler to reach inside the intersection, throw out the nested union member, then create a new intersection type. I will go see if there are other issues related to this. I think it comes up every couple of weeks. |
The intersection type doesn't really have anything to do with the behavior here. Consider: type Foo = { a: string } | { b: string };
let foo: Foo;
if (foo.a) {
} We currently report an error because We might consider an alternate behavior in So, for purposes of property access, the type type Foo = { a: string } | { a: string, b: string } | { a: number, c: number }; would behave like type Foo = { a: string | number, b: string | undefined, c: number | undefined }; and it would require type guards to access the values in |
That kind of type guard is very error-prone since it depends on the other types in the union not having a property with the same name as an implementation detail: interface I { a: string; }
interface J { b: string; }
class C implements I {
a = "foo";
private b = 42;
}
var c: I | J = new C();
c.b && c.b.toUpperCase(); // would throw TypeError at runtime if allowed Allowing this would mean you could no longer add or rename an object's internal properties without worrying about whether it might conflict with a union somewhere. (EDIT: fixed method name) |
@jeffreymorlan |
@blakeembrey I meant |
I know, but the comment still stands - TypeScript would error on Edit: I see your concern. I just tried out some tests. Edit 2: I know it's probably a bit of work, but maybe it could work with existing flow control then? E.g. Edit 3: Given all this, I'm not sure it's relevant now. I don't think there's anything stopping it from happening today. If you want to use one side of the union today, you'd need to rely on type coercion which removes type safety already. How are you using types like the one you mentioned above in today's code? |
@blakeembrey I believe we do the distributive rules correctly now - can you confirm? |
I believe this is classified as unsound unless there's been a recent change to type checking for the |
There's |
This is allowed today with the (hopefully) clearer |
TypeScript Version:
1.8 / 1.9
Code
Expected behavior:
baz.a
should be accessible, at least in 1.9 with thenull
checks.Actual behavior: Property
a
does not exist on typeEdit: To clarify, I suspected it would have let me use it in the
if
condition in 1.9 since the introduction ofstrictNullChecks
and flow control sounds like it would allow this functionality to pass.The text was updated successfully, but these errors were encountered: