I'm trying to cover types cases when function fireEvent
accept the function name what should be called at first param and on second function params.
const eventsSchema = {
onComplete: ({ id, name }: { id: number, name: string }): void => {
// do some things
},
onStart: ({ name }: { name: string }): void => {
// do some things
},
onCancel: ({ reason }: { reason: string }): void => {
// do some things
},
}
function fireEvent<K extends keyof typeof eventsSchema>(event: K, params: Parameters<typeof eventsSchema[K]>[0]) {
return eventsSchema[event](params);
}
as result works fine
fireEvent('onComplete', { id, name }); // ok as expected
fireEvent('onComplete', { name }); // error as expected
fireEvent('onStart', { id }); // ok as expected
fireEvent('onStart', { id, reason }); // error as expected
fireEvent('onCancel', { reason }); // ok as expected
fireEvent('onCancel', { name, id }); // error as expected
BUT I have an error inside fireEvent
How to cover this?
CodePudding user response:
Consider this example:
const eventsSchema = {
onComplete: ({ id, name }: { id: number, name: string }): void => {
// do some things
},
onStart: ({ name }: { name: string }): void => {
// do some things
},
onCancel: ({ reason }: { reason: string }): void => {
// do some things
},
}
function fireEvent<K extends keyof typeof eventsSchema>(event: K, params: Parameters<typeof eventsSchema[K]>[0]) {
const args = params
const fn = eventsSchema[event];
}
fn
expects an intersection of all methods arguments, because it is the safest way to call this function. You don't know inside a function scope which argument was provided.
Also, TypeSCript is more efficient if you use pure functions. fireEvent
is not pure, because it depends on eventsSchema
.
SO, in your example it is a good idea to use currying:
const eventsSchema = {
onComplete: ({ id, name }: { id: number, name: string }): void => {
// do some things
},
onStart: ({ name }: { name: string }): void => {
// do some things
},
onCancel: ({ reason }: { reason: string }): void => {
// do some things
},
}
type Schema = typeof eventsSchema
type Fn = (...args: any[]) => void
const withSchema = <
S extends Record<string, Fn>
>(schema: S) =>
<Name extends keyof S>(name: Name, ...params: Parameters<S[Name]>) => schema[name](...params)
const fireEvent = withSchema(eventsSchema)
fireEvent('onComplete', { id: 42, name: '' }) // ok
fireEvent('onComplete', { id: 42, name: 2 }) // expected error
MOre information about this techniue you can find in my blog