0

Say I have the following two types:

export type CollectionNames = 'twitter:tweets' | 'twitter:users' | 'twitter:metadata-cashtag'

export type CollectionType<T extends CollectionNames> = 
    T extends 'twitter:tweets' ? Tweet :
    T extends 'twitter:users' ? User :
    T extends 'twitter:metadata-cashtag' ? CashtagMetadataDb :
    never

I feel this is very clunky and I'm not very keen on having the strings twice. Also it's possible to legally misspell them in the latter type.

Is there any way to create these dynamically from an object such as this:

typings = {
    'twitter:tweets': Tweet,
    'twitter:users': User,
    'twitters:metadata-cashtag': CashtagMetadataDb
}

The idea is that multiple modules will have their own CollectionType type which is then aggregated into one CollectionType in the importing root module. So if I have two modules Coin and Twitter imported using * as, it looks something like this:

type CollectionName = Twitter.CollectionNames | Coin.CollectionNames

type CollectionType<T extends CollectionName> = 
    T extends Twitter.CollectionNames ? Twitter.CollectionType<T> :
    T extends Coin.CollectionNames ? Coin.CollectionType<T> :
    never

These will then be used in a function like so where the types are of the latter kind (Collection here is from MongoDB):

async function getCollection<T extends CollectionName> (name: T): Promise<Collection<CollectionType<T>>>
2
  • Can you please post an example how CollectionType is used? Commented Aug 30, 2018 at 8:48
  • Updated the question with edits Commented Aug 30, 2018 at 9:33

1 Answer 1

3

I think in this case you don't need conditional types at all; you can do this with keyof and lookup types instead. You probably could create an object like typings and derive a type from it, but unless you need that object for something at runtime (and have objects of type Tweet, User, etc lying around) I'd say you should just make an interface type like this:

export interface Collections {
  'twitter:tweets': Tweet,
  'twitter:users': User,
  'twitter:metadata-cashtag': CashtagMetadataDb
}

Then, your CollectionNames and CollectionType types can be defined in terms of that type:

export type CollectionNames = keyof Collections;
export type CollectionType<K extends CollectionNames> = Collections[K];

You can verify that the above types act the same as your definitions. In the case that you have multiple modules that have exported Collections types, you can simply merge them using interface extension and re-derive CollectionNames and CollectionType from it:

export interface Collections extends Twitter.Collections, Coin.Collections {}
export type CollectionNames = keyof Collections;
export type CollectionType<K extends CollectionNames> = Collections[K];

Hope that helps. Good luck!

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

1 Comment

This is fantastic and fits what I need perfectly! Thank you for your time :)

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.