Typescript: Get discriminated union variant type

Created on 27 Oct 2020  路  3Comments  路  Source: microsoft/TypeScript

Search Terms

discriminated union, variant, mapped types

Suggestion

We can get a property type from object without any surprise:

type Payload = { foo: string }
type Foo = Payload['foo'] // string

But if the object is a discriminated union, we can only get a type of a member that repeats on all variants

type Payload = (
  {
    result: 'success'
    data: number
  } |
  {
    result: 'error'
    errorCode: 'invalid-token' | 'expired-token'
  }
)

type Errors = Payload[ ???? ] // we can't =(

The only way at this moment is extracting the type into a new one:

type PayloadError = 'invalid-token' | 'expired-token'
type Payload = (
  {
    result: 'success'
    data: number
  } |
  {
    result: 'error'
    errorCode: PayloadError
  }
)

type Errors = PayloadError

Use Cases

Extracting a type from a variant has two problems:

  • is inconvenience if we are using an external library
  • if we want this type only on a specific moment that would be better expressed without a new type (same use case of using Payload['foo'] instead of creating a Foo type)

Examples

I really don't know how could be the syntax. Maybe could be something like

type PayloadError = Payload['result'] when 'error'
type Errors = (Payload['result'] when 'error')['errorCode']

Related

The suggestion https://github.com/microsoft/TypeScript/issues/39103 is similar, but for a different use case.
I'm suggesting to get the variant type _outside_ of the declaration, and this suggestion want to get the variant type _inside_ of the type + ternary operator in order to write some shortcuts on the declaration type.

Checklist

My suggestion meets these guidelines:

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

Most helpful comment

Or like this:

type Errors = (Payload & { result: "error" })["errorCode"];

All 3 comments

You can do this already with conditional types:

type OfResult<T extends { result: string }, K extends string> = T extends { result: K } ? T : never
type Errors = OfResult<Payload, 'error'>['errorCode']

Or like this:

type Errors = (Payload & { result: "error" })["errorCode"];

I didn't know that it's possible with these approaches. Thanks.
I'm closing my issue because I think that will be overengineering adding my suggestion, that will be just a syntax sugar .

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Roam-Cooper picture Roam-Cooper  路  3Comments

dlaberge picture dlaberge  路  3Comments

zhuravlikjb picture zhuravlikjb  路  3Comments

weswigham picture weswigham  路  3Comments

manekinekko picture manekinekko  路  3Comments