Poked around SO looking for an answer for this but turned up dry – in my GraphQL server, I'm trying to add better typing around my subscriptions because the types I pulled from NPM use any
too much. I wrote a map outlining my subscriptions, where each map value contains the channel name, subscription name, and an object class that is used to type the payload:
class ConversationCreatedEvent {
conversationID: string;
userID: string;
}
export const Subscriptions = {
conversationCreated: {
name: 'onConversationCreated',
event: ConversationCreatedEvent,
channelName: 'CONVERSATION_CREATED',
},
};
I'd like to write a method that allows you to publish subscriptions that takes two arguments: the subscription ID (map key), and an object conforming to an instance of class type specified in the event
field.
So far, I have:
const publishSubscription = <T extends keyof typeof Subscriptions>(
name: T,
data: typeof Subscriptions[T]['event']
) => {
// TODO: implement
};
However, this approach makes the second argument typeof ConversationCreatedEvent
, and expects keys such as length
, name
, apply
, etc. (i.e. the keys you'd find in a class object, not an instance of the class). I need this to accept the actual class members, such as conversationID
.
Hope this all makes sense. I feel like it's very straightforward, there's just something small I'm missing. I'm still learning mapped types.
Any help is appreciated!!
const Subscriptions
has the class constructor as a value. So:
typeof Subscriptions['consoversationCreated']['event']
is the same as:
typeof ConversationCreatedEvent
Which means that you have a class constructor type, and you want to get the instance type from it.
Typescript has a built in utility type to do just that called InstanceType
.
class Foo {}
// The following type aliases are equivalent
type FooInstance = Foo
type InstanceTypeFoo = InstanceType<typeof Foo>
Or in your case:
InstanceType<typeof Subscriptions[T]['event']>