-
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
Narrowing discriminated unions with union discriminant properties #31404
Comments
I think this is a design limitation in discriminant narrowing which is effectively a top-down process, rather than bottom up. When narrowing for the negation of a discriminant check - so when something like Knowing that Knowing that Only using the composition of the two is it possible to rule out For example, the checker knows that function test(anOption: OptionUnion): void {
if (anOption.x === true) {
acceptOption1(anOption);
} else if (anOption.x === false || anOption.x === undefined) {
acceptOption2(anOption);
} else {
anOption.x // x is never
acceptNever(anOption);
}
} Ideally you want to identify that any object that has a property of type There may be some changes that could be done in the checker to support this; someone on the team would need to look into it. Two workarounds for now:
interface Option1 { x: true; }
interface Option2 { x: false; }
interface Option3 { x: undefined; }
type OptionUnion = Option1 | Option2 | Option3;
acceptNever(anOption.x); // ok |
I have a case that may be more related to #20863 from a technical standpoint, but since it is specific to undefined, I thought I'd add here. I'd like to have an interface that conditionally has a set of additional optional properties based on the truthiness of an optional property.
The use case is for a React component where for ~80% of the use cases I don't want to have to pass in Instead
does not throw an error, when I expect it to. I can not use the first workaround, because then I would manually have to still include the optional prop as Let me know if I should open a separate issue. Since this is close to many open issues, I didn't want to add another to the list! EDIT: By the way, I am using the |
@jack-williams I'm not sure I understand why this is a design limitation. The checker does know how to narrow the discriminant property itself. Couldn't the object's type be narrowed at the same time? |
TypeScript Version: 3.5.0-dev.20190514
Search Terms: undefined discriminated union discriminant discriminator unions type guard control flow narrowing
Code
Expected behavior:
No errors.
Actual behavior:
acceptNever(anOption);
has an error:Argument of type 'Option2' is not assignable to parameter of type 'never'. ts(2345)
Note that the code works correctly if the discriminator is not a property:
Playground Link:
http://www.typescriptlang.org/play/index.html#src=%2F%2F%20--strictNullChecks%0D%0A%0D%0Ainterface%20Option1%20%7B%0D%0A%09x%3A%20true%3B%0D%0A%7D%0D%0A%0D%0Ainterface%20Option2%20%7B%0D%0A%09x%3A%20false%20%7C%20undefined%3B%0D%0A%7D%0D%0A%0D%0Atype%20OptionUnion%20%3D%20Option1%20%7C%20Option2%3B%0D%0A%0D%0Afunction%20acceptOption1(x%3A%20Option1)%3A%20void%20%7B%7D%0D%0A%0D%0Afunction%20acceptOption2(x%3A%20Option2)%3A%20void%20%7B%7D%0D%0A%0D%0Afunction%20acceptNever(x%3A%20never)%3A%20void%20%7B%7D%0D%0A%0D%0Afunction%20test(anOption%3A%20OptionUnion)%3A%20void%20%7B%0D%0A%09if%20(anOption.x%20%3D%3D%3D%20true)%20%7B%0D%0A%09%09acceptOption1(anOption)%3B%0D%0A%09%7D%20else%20if%20(anOption.x%20%3D%3D%3D%20false%20%7C%7C%20anOption.x%20%3D%3D%3D%20undefined)%20%7B%0D%0A%09%09acceptOption2(anOption)%3B%0D%0A%09%7D%20else%20%7B%0D%0A%09%09acceptNever(anOption)%3B%0D%0A%09%7D%0D%0A%7D%0D%0A
Related Issues:
#14471 is also using
undefined
as a discriminant, although the linked issue is restricting itself to talking about mapped typesThe text was updated successfully, but these errors were encountered: