简体   繁体   中英

Typescript discriminated unions in an object depending on string

I initially thought my problem would be solved by conditional types, but upon further inspection it seems like discriminated unions would work better for my case.

These examples get really close to what I want, but I'm still confused on how to implement it in my real world example.

Say I have an interface, in this case it's the data I get back after logging in with Passport.js:

export interface UserInterface {
  id: number
  username: string
  photos: {
    values: string
  }[]
  provider: 'twitter' | 'github'
  _json: TwitterProfileInterface | GithubProfileInterface
}

In the above, id , username and photos are all interchangeable. However, the type of _json depends on the value of provider . So if provider is twitter , then _json will be TwitterProfileInterface .

Is there any way to make this automatic with Typescript?

EDIT: Example code:

I'm running a switch statement to check what type of User Profile I received:

const user = {
  id: 1,
  username: "User",
  photos: [],
  provider: "twitter",
  _json: {
    // a bunch of data specific to the "twitter" provider
  }
};

switch(user.provider){
  case 'twitter':
    // this case will give types from both TwitterProfileInterface and GithubProfileInterface
    break;
  case 'github':
      // this case will give types from both TwitterProfileInterface and GithubProfileInterface
    break;
}

Even though I have a switch statement, the fact that the value I use for the switch is contained outside of either TwitterProfileInterface and TwitterProfileInterface then both cases will include both types, rendering my types pointless.

Use a conditional type and parameter, like this:

interface TwitterProvider { foo: number }
interface GithubProvider { foo: string }

export type UserInterface<P extends 'twitter' | 'github'> = {
  id: number
  username: string
  photos: {
    values: string
  }[]
  provider: P
  _json: P extends 'twitter' ? TwitterProvider : P extends 'github' ? GithubProvider : {}
}

let foo: UserInterface<'twitter'> = {
  id: 1,
  username: 'foo',
  photos: [{values: 'foo'}],
  provider: 'twitter',
  _json: { foo: 1 }
}

Live, Interactive Example

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM