最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

typescript - Can you require functions to have matching arguments, if they're defined within an object within a record?

programmeradmin5浏览0评论

In a record of objects where each object has a fn function and a genSetKey function:

const cacheableFunctions = [
  getUserProfile: {
    fn: async (id: string) => {},
    genSetKey: (id: string) => id,
  },
  getUserSchedule: {
    fn: async (id: string, day: Date) => {},
    genSetKey: (userId: string, day: Date) => userId + day.toDateString()
  }
]

(The objects are called "invalidators" in the upcoming code snippets, since the goal of this project is to help invalidate old cache entries)

I want to require each genSetKey's arguments to match its fn's arguments.

This is what I tried at first:

type invalidator<TArgs extends unknown[]> = {
  fn: (...args: TArgs) => any;
  genSetKey: (...args: TArgs) => string;
};

type invalidatorRecord<T extends Record<string, unknown>> = {
  [K in keyof T]: T[K] extends invalidator<infer U> ? invalidator<U> : never;
};

This works if you specify the types explicitly, but it doesn't work if you try to infer them:

const explicitExample: invalidatorRecord<{
  updateName: invalidator<[string]>;
  updateAge: invalidator<[number, Date]>;
}> = {
  updateName: {
    fn: (name: string) => {/* some biz logic */},
    genSetKey: (name: string) => `name:${name}`,
  },
  updateAge: {
    fn: (age: number, date) => {/* some biz logic */},
    genSetKey: (age: string, date) => `age:${age}`,
    // ^ correctly shows type error for wrong argument type
    // (Type '(age: string) => string' is not assignable to type '(args_0: number) => string')
  },
};

// little utilty to test inference
const makeInvalidatorRecord = <T extends Record<string, unknown>>(
  invalidatorObj: invalidatorRecord<T>,
) => invalidatorObj;

const inferredExample = makeInvalidatorRecord({
  updateName: {
  // ^ Type '{ fn: (name: string) => void; genSetKey: (name: string) => string; }' is not assignable to type 'never'.
    fn: (name: string) => {},
    genSetKey: (name: string) => `name:${name}`,
  },
  updateAge: {
  // ^ Type '{ fn: (name: string) => void; genSetKey: (name: string) => string; }' is not assignable to type 'never'.
    fn: (age: number) => {},
    genSetKey: (age: number) => `age:${age}`,
  },
});

It seems like type of each invalidator is being inferred as never, which makes me think that they're failing the T[K] extends invalidator<infer U> ? conditional. But they seemingly match the shape of the invalidator type!

Link to TS playground

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论