Home > Software engineering >  Overload all methods inside class through generic
Overload all methods inside class through generic

Time:07-03

Is there a generic way to pass into a constructor all the keys and what each data is returned by each key by all the functions within?

Currently I do this:

class Redis {
  get() {

  }
  set() {

  }
  // ... many more methods
}

const redis = new Redis();
type IEmailKey = `unverified-email:${number}`;
type IEmailData = string;
type IDidRemindKey = `did-remind:${number}`;
type IDidRemindData = boolean;

type IStrictGet = {
  (key: IEmailKey): Promise<IEmailData | null>;
  (key: IDidRemindKey): Promise<IDidRemindData | null>;
};

type IRedisSetParams = Parameters<Redis['set']>;
type IStrictSet = {
  (
    key: IEmailKey,
    value: IEmailData,
    options?: IRedisSetParams[2]
  ): Promise<'OK' | null>;
  (
    key: IDidRemindKey,
    value: IDidRemindData,
    options?: IRedisSetParams[2]
  ): Promise<'OK' | null>;
};

// This has key's typed along with the type of data each key contains.
type IStrictRedis = Omit<Redis, 'get' | 'set'> & {
  get: IStrictGet;
  set: IStrictSet;
};

This allows me to do redis.get('unverified-email:123') and redis.set('unverified-email:123', '[email protected]') and redis.get('did-remind:123') and redis.set('did-remind:123', true).

I was hoping for a simple way to pass generic to class Redis which handles it, here is pseudocode:

const redis = new Redis<{
  [`unverified-email:${number}`]: string;
  [`did-remind:${number}`]: boolean;
}>();

CodePudding user response:

You could declare Redis like this:

class Redis<T extends Record<string, any>> {
  get<K extends keyof T>(key: K): T[K] {
    return {} as any
  }
  set<K extends keyof T, V extends T[K]>(key: K, value: V): Promise<'OK' | null> {
    return {} as any
  }
}

And construct it similiar to what your pseudcode looked like. Only difference here is that we have to prepend key: before the string literal.

const redis = new Redis<{
  [key: `unverified-email:${number}`]: string
  [key: `did-remind:${number}`]:  boolean
}>();

Some test-cases:

const a = redis.get("unverified-email:123")
//    ^? const a: string

const b = redis.get("did-remind:1")
//    ^? const b: boolean

redis.set("unverified-email:123", "abc")
redis.set("did-remind:1", true)
redis.set("unverified-eail:123", "abc") // Error: "unverified-eail:123"' is not assignable to parameter of type '`unverified-email:${number}`
redis.set("did-remind:1", "abc") // Error: Argument of type 'string' is not assignable to parameter of type 'boolean'

Playground

  • Related