Home > Software design >  How to deduce argument types for the callback?
How to deduce argument types for the callback?

Time:10-26

I have an arrow function, it is used for cached queries. It accepts cache key and a callback function with it's arguments. At first this function looks up in Redis with the specified key, and if there is no cache by that key - it executes the callback.

Everything works, but I don't like the way how my declaration looks like: it's unsafe (you can pass argument of wrong type), and on the other hand I have a disabled linter warning. So here is my code (only the relevant part remaining):

import { RedisClient } from './../redis/redisClient';

export type CachedQueryPromiseResult<T> = { result?: T, error?: object };
export type CachedQueryPromise<T> = Promise<CachedQueryPromiseResult<T>>;

export const cachedDbQuery = async <T,>(
    opts: { cacheKey: string, ttlSec: number, redis: RedisClient },
    callback: (...cbArgs: any) => Promise<T>,
    ...args: any
): CachedQueryPromise<T> => {
    const response: CachedQueryPromiseResult<T> = {};

    const cache = await opts.redis.impl.get(opts.cacheKey);
    if (cache) {
        response.result = JSON.parse(cache) as T;
        return response;
    }

    // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
    const result = await callback(...args);
    if (result) {
        await opts.redis.impl.setex(opts.cacheKey, opts.ttlSec, JSON.stringify(result));
        response.result = result;
    }

    return response;
};

How can I declare this callback? Now it accepts any, but I would like it to deduct the required arguments with the types. It is not safe now.

cachedDbQuery({ redis: redisClient, cacheKey: 'TESTAR', ttlSec: 120 }, vkuClient.getLicenseInfo.bind(vkuClient), identificator)

getLicenseInfo accepts string, but with the current implementation I can pass any type to it.

What could you suggest?

CodePudding user response:

Add another generic parameter, as TS will be able to infer this one as well:

export const cachedDbQuery = async <T, A extends ReadonlyArray<unknown>>(
    opts: { cacheKey: string, ttlSec: number, redis: RedisClient },
    callback: (...cbArgs: A) => Promise<T>,
    ...args: A
): CachedQueryPromise<T> => {

However, since this one represents the arguments, it must have a constraint of an array.

Playground (severely simplified)

  • Related