Home > database >  Function parameter type depends on another parameter
Function parameter type depends on another parameter

Time:09-01

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

enter image description here

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

Playground

MOre information about this techniue you can find in my blog

  • Related