Home > Blockchain >  TypeScript using generic type of generic type as value type
TypeScript using generic type of generic type as value type

Time:10-20

The question is a bit hard to explain without showing an example, so let's look at this Message type that will be extended and used as base type for other types.

interface Message<E extends string = string, P = any> {
  topic: E;
  params: P;
}

I have this interface for indicading message type. The topic and parameters are generic to make sure the interface could be extend for different cases like:

interface OrderParams {
  userId: string;
  orderId: string;
}

interface CreateOrderMessage extends Message<'orders.create', OrderParams> {}
interface UpdateOrderMessage extends Message<'orders.update', OrderParams> {}
...

type CustomMessage = CreateOrderMessage | UpdateOrderMessage | ...;

This allows me to add strict typing for topic and params for different topic types which I could use with a class:

class PubSub<T extends Message = Message> {
  publish(message: T): void;
  subscribe(topic: string): void;
}

If we pass CustomMessage as a generic type to PubSub<> it will check typings for publish method, but I also want to make sure topic parameter of subscribe method is also type checked using E generic type of T extends Message generic type.

So, is there a way to somehow extract generic type of another generic type so I could write something like below?

subscribe(topic: GenericOf<T, 0>);             // 1st generic of T type
subscribe(topic: TypeOfObjectKey<T, 'topic'>); // Type of 'topic' property of T type

CodePudding user response:

subscribe(topic: T['topic']) will ensure the topic parameter of subscribe is the same type as the topic property of the interface passed into the generic. Here's an example derived from the code you've provided in your question (I've made it error so that the underlying type is easily visible):

interface Message<E extends string = string, P = any> {
  topic: E;
  params: P;
}

interface OrderParams {
  userId: string;
  orderId: string;
}

interface CreateOrderMessage extends Message<'orders.create', OrderParams> {}
interface UpdateOrderMessage extends Message<'orders.update', OrderParams> {}

type CustomMessage = CreateOrderMessage | UpdateOrderMessage;

class PubSub<T extends Message = Message> {
  publish(message: T): void {};
  subscribe(topic: T['topic']): void {};
}

new PubSub<CustomMessage>().subscribe('')
                                      ^^
Argument of type '""' is not assignable to parameter of type '"orders.create" | "orders.update"'.

TypeScript Playground

  • Related