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