Reputation: 2073
I have below TypeScript code (link to playground):
type MyCallback<T> = (s: string, payload: T) => void;
interface IActions {
do1: MyCallback<number>;
do2: MyCallback<string>;
[key: string]: (s: string, payload: any) => void;
}
function convert<T extends { [key: string]: (s: string, payload: any) => void }>(callbackMap: T) {
const result: { [key: string]: <U>(payload: U) => void } = {};
Object.keys(callbackMap).forEach(key => {
if (typeof callbackMap[key] === 'function') {
result[key] = callbackMap[key].bind(null, "data");
}
})
return result;
}
const maps = convert<IActions>({
do1: (s: string, payload: number) => {
//
},
do2(s: string, payload: string) {
//
}
});
maps.do1(1); // valid
maps.smth("1"); // should be type-check error, but TS thinks it's valid
What I'm trying to do is to create a function, which accepts an object via an interface. The function converts all methods from the object to a new object, where all methods have one fixed parameter (via bind
method). In other words, I want to convert this interface
interface IActions {
do1: (state: string, payload: number);
do2: (state: string, payload: string);
.....
}
to
interface IActions {
do1: (payload: number);
do2: (payload: string);
....
}
I want to make it generic, so it converts any interface based on generic parameter.
The issue with my current approach is that I don't have any intellisense and type checking for my maps
object.
Is it possible to modify my convert
function in such a way, that return type is automatically inferred by the incoming interface? In other words, I have full type checking and intellisense for return value (maps
in my case).
Upvotes: 3
Views: 1189
Reputation: 249646
The fact that maps.smth
is valid is due to the explicit index signature on the result. What you need here is a mapped type to map over the properties of IActions
to a new type containing the modified methods. To create the new method signature we can use a conditional type to extract the rest of the parameters (skip the first one)
type MyCallback<T> = (s: string, payload: T) => void;
interface IActions {
do1: MyCallback<number>;
do2: MyCallback<string>;
}
function convert<T extends Record<keyof T, (s: string, payload: any) => void>>(callbackMap: T) {
const result: Record<string, (...a: any[]) => any> = {}
Object.keys(callbackMap).forEach(key => {
if (typeof callbackMap[key as keyof T] === 'function') {
result[key] = callbackMap[key as keyof T].bind(null, "data");
}
})
return result as {
[P in keyof T]: T[P] extends (s: string, ...p: infer P) => infer R ? (...p: P) => R : never;
};
}
const maps = convert<IActions>({
do1: (s: string, payload: number) => {
//
},
do2(s: string, payload: string) {
//
}
});
maps.do1(1); // valid
maps.do1("1"); //err
maps.smth("1"); // should be type-check error, but TS thinks it's valid
Upvotes: 1