0

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.

7
  • I don't see much of a use for conditional types here; this looks more like you need a discriminated union, such as this for example. Does that meet your needs? If so, could you maybe edit the question to remove the dependency on conditional types? If not, could you elaborate in your question what those needs are? Good luck! Commented Apr 8, 2021 at 20:16
  • @jcalz That does look more like what I want to do, but how do I make it so the type is decided outside of my union? Commented Apr 8, 2021 at 20:27
  • 1
    Sorry, I don't understand the question. What does "type is decided outside of my union" mean? Could you show me with a minimal reproducible example? Commented Apr 8, 2021 at 20:27
  • Is the example in the OP not enough? In my case the "switch" (provider) is not contained within the two types I'm switching between unlike the Typescript example. So while I can use a Switch Statement with provider I don't see how that will have effect on which _json type I use. Commented Apr 8, 2021 at 20:32
  • 1
    It seems you've already accepted an answer that isn't using discriminated unions... if you're interested, the discriminated union version looks like this. Commented Apr 9, 2021 at 1:17

1 Answer 1

2

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

Sign up to request clarification or add additional context in comments.

3 Comments

How does this work for the interface? Typescript is throwing errors if I stick this in PassportUserInterface
Oooooh, I see how this works now. That's really helpful!
If you want to add a fallback, change the : {} to : FallbackType. To add more options, add another chain and change the extends '...' | '...' directive.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.