Hi I create a plugin and a function to callPlugin action.
export class Plugin {
actions = {
'join': ({ groupId }: { groupId: string }) => {
},
'addPoint': ({ point }: { point: number }) => {
}
}
}
export type PluginParams<T> = T extends {
actions: Record<infer X, (param: infer Params) => any>
} ? {
action: X,
data: Params, // how do i do mapping type ?
} : never;
function callPlugin<T>(v: PluginParams<T>) {
console.log(v);
}
callPlugin<Plugin>({
action: 'addPoint',
data: {
// it has an error here how do i fix that
// when action is `addPoint` it should only require point.
point: 100
}
})
I can infer the params from the plugin, but I don't know how to use them with the mapping type. I simply want it to map action name with the parameters from the action function
I already check all similar question on stackoverflow but i can't still find a solution.
Thank you.
Since you're going to manually specify the generic type argument when you call
callPlugin(), likecallPlugin<Plugin>({⋯}), then there you want to definePluginParams<T>so thatPluginParams<Plugin>becomes a discriminated union of the acceptable inputs of the form:Here's one way to do that:
I'm constraining
Tso that it has anactionsproperty of typeRecord<keyof T["actions"], (p: any) => any>, so that it only accepts things suitablyPlugin-like. Note that instead of the recursivekeyof T["actions"]you could probably usestring, but sometimes that doesn't play nicely with interface types (see Subtype of Record<string, X> without the index signature).Then all we do is map over the properties of
T["actions"]and transform them to object with anactionanddataproperty of the appropriate type, and subsequently index into it with the union of keys to get a union out. This sort of indexed-mapped-type of the form{[P in K]: F<P>}[K]so that a union inK(e.g.,K1 | K2 | K3) produces a union in the output (e.g.,F<K1> | F<K2> | F<K3>) is called a "distributive object type" and is described in microsoft/TypeScript#47109.You can verify that
PluginParams<Plugin>becomes the desired discriminated union, and that your call works as desired if you definecallPluginto acceptPluginParams<T>:Do note that this is a somewhat nonstandard way of writing and calling generic functions. It requires that the caller manually specify the type argument, which is usually inferred. I'd expect that either your
callPlugin()function should also take apluginargument from whichTcan be inferred, or you should refactor so that all the generic stuff is dispensed with before anyone callscallPlugin(), so that nobody has to remember to specify the type argument manually. This is technically out of scope for the question as asked, so I won't delve further into this, other than to say to be careful that your code can be properly consumed by callers.Playground link to code