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
Idea for allowing destructuring non-common property of discriminated unions #46318
Comments
|
Just some questions to help flesh this out: Should giving non-common properties default values be disallowed? type Union = { kind: 'x' } | { kind: 'y' ; value: number }
function example({ kind, value = 42 }: Union) {
// is this allowed? ^^^^
// if so would this be allowed now?
value;Should destructuring non-common properties be allowed? type Union = { kind: 'x' } | { kind: 'y' ; value: { a: number, b: number} }
function example({ kind, value: { a, b } }: Union) {
// Use of 'a' and 'b' not allowed because they may not exist
a;
b; |
|
Thanks for your interest
Theoretically that syntax could be allowed, but that'd have little value. You would still need to perform an appropriate narrowing before accessing type Union = { kind: 'x' } | { kind: 'y' ; value: number }
function example(_: Union) {}
const badObj = {
kind: "x" as const,
value: "Hello!"
};
example(badObj);
I don't think this one can be allowed. A runtime error would be inevitable because destructuring occurs before narrowing is done. |
They could still provide value in cases like the following: type Union = { kind: 'x' } | { kind: 'y' ; value?: number } // note how `value` is optional
function example({ kind, value = 42 }: Union) {
if (kind === 'y') {
value; // it's `number` rather than `number | undefined`
}
if (kind == 'x') {
value; // this is not allowed
}
}
Actually destructuring properties that don't exist isn't a runtime error; they just get assigned |
Indeed
Yeah that's exactly why this suggestion is possible. But your example is a nested one, meaning that it might try destructuring undefined into |
|
I would love this idea to be implemented, thx for your suggestion! |
|
Isn't this available since TS 4.6? Here are the release notes. |
No, what was added is that when you destructure common property (exist in all of the types in the union) from object it can be narrowed. function foo(): {a: string} | {b: string} {
return {a: ''}
}
const { a, b } = foo() // error TS2339: Property 'a' does not exist on type '{ a: string; } | { b: string; }'. |
|
Oh, I didn't realize that little detail. Thanks for pointing that out. |
|
This feature would be very very appreciated |
uhyo commentedOct 12, 2021
•
edited
Suggestion
type narrowing discriminated union destructing
does not exist on typeMy suggestion meets these guidelines:
Recent & future versions of TypeScript are strengthening support for discriminated unions. Looking at the most recent effort #46266 , it seems that we want to facilitate combination of destructuring assignment and discriminated unions. The ongoing work is wonderful, but it is one step behind being fully usable everywhere; in order to utilize that brand new feature, a common property (
payloadin that PR) must exist on every union constituents. Generally, this is not the case.So this suggestion shows off idea on how we can extend the feature to discriminated unions of other shapes.
Suggestion: destructuring a property that might exist (that exists on only some of union constituents) is allowed, but access to such variable is not allowed until proper narrowing is done.
Such a variable that causes an error on access is not something novel;
letvariables that aren't assigned yet already behave similarly.As shown in above code sample, discriminated union constituents may not always have properties of the same name. Rather, given that discriminated unions tend to be used to express βorβ logics, such a situation feels rare.
In addition, even if all constituents could have a property of the same name, giving them different names is often better.
In this example, we could rename both
valueanderrortopayloadso that it can benefit from the ongoing PR, but the new name feels less descriptive.Placeholder property definitions could be added so that all property names become common, but I don't really feel it is the right way to go:
This works but it allows users to easily skip inspecting
type, particularly thanks to the optional chaining operator. I have seen several bugs due to not properly handling thetype, so in my most recent project I decided to remove these placeholder?: undefineddefinitions from discriminated unions and force users to inspecttypebefore accessing each constituent's contents.Therefore, a solution that allows us to use non-common property names and also utilize destructuring assignments would be ideal.
Above two types (
Option<T>andResult<T>) are the most basic use cases; there should be a lot more in the wild.The text was updated successfully, but these errors were encountered: