question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Idea for allowing destructuring non-common property of discriminated unions

See original GitHub issue

Suggestion

🔍 Search Terms

type narrowing discriminated union destructing does not exist on type

✅ Viability Checklist

My suggestion meets these guidelines:

  • This wouldn’t be a breaking change in existing TypeScript/JavaScript code
  • This wouldn’t change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn’t a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript’s Design Goals.

⭐ Suggestion

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 (payload in 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.

type Option<T> = {
  kind: 'some';
  value: T;
} | {
  kind: 'none';
};

// destructuring 'value' is allowed ↓
function unwrap<T>({ kind, value }: Option<T>): T {
  // use of 'value' is not allowed here because it may not exist.
  value;

  if (kind === 'some') {
    // use of 'value' is now allowed.
    return value;
  }
  throw new Error('unwrap: expected some');
}

Such a variable that causes an error on access is not something novel; let variables that aren’t assigned yet already behave similarly.

📃 Motivating Example

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.

type OkResult<T> = {
  type: "ok";
  value: T;
};
type ErrorResult = {
  type: "error";
  error: Error;
};
type Result<T> = OkResult<T> | ErrorResult;

In this example, we could rename both value and error to payload so 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:

type OkResult<T> = {
  type: "ok";
  value: T;
  error?: undefined;
};
type ErrorResult = {
  type: "error";
  value?: undefined;
  error: Error;
};

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 the type, so in my most recent project I decided to remove these placeholder ?: undefined definitions from discriminated unions and force users to inspect type before 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.

💻 Use Cases

Above two types (Option<T> and Result<T>) are the most basic use cases; there should be a lot more in the wild.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:45
  • Comments:9 (2 by maintainers)

github_iconTop GitHub Comments

2reactions
Luk-zcommented, Apr 26, 2022

This feature would be very very appreciated

2reactions
benhason1commented, Apr 12, 2022

I would love this idea to be implemented, thx for your suggestion!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Discriminated Unions and Destructuring in TypeScript
Discriminated unions in TypeScript can go awry when you use object or array destructuring. Learn why it happens and what you can do...
Read more >
How to make Discriminated Unions work with destructuring?
By using destructuring at level of function argument we loose connection between kind and data . So switch by kind is not narrowing...
Read more >
Overview - TypeScript
Previously, a non-disciminated union wouldn't have any excess property ... TypeScript 3.2 also allows destructuring a rest binding from a generic variable.
Read more >
Detecting never-before-seen event types - Nicholas Blumhardt
Next Using attributes to control destructuring in Serilog ... In their issue report they show how a nice clean discriminated union type like...
Read more >
Handbook Page - Microsoft Open Source
accessing the property 'toLowerCase' // on 'foo' and then calling it foo. ... TypeScript will only allow you to do things with the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found