Facundo Rodriguez
Facundo Rodriguez

Reputation: 27

How to inherit narrowed type from function argument?

I have a function that accepts as an argument another function. The argument is an union type of functions that also may have or may have not arguments.

type innerFn = () => void | (arg: string) => void | (arg: number) => void | (arg: {}) => void

function mainFn (innerFn: InnerFnType) {
   function run(arg?: *any*) {
      innerFn()
   }
   return run
}

I want to know if the run argument can be inherited based on the function that is passed to mainFn.

The use case is:

const concreteInnerFn: InnerFnType = (arg: string) {
     do something
}

const run = mainFn(concreteInnerFn)

run("hello")  // OK
run(25) // ERROR
run({hello: true}) // ERROR

ie if I use (arg: string) => void as the innerFn, when I try to use run() it should ask for an argument of type string.

Hope its clear enough

Upvotes: 1

Views: 514

Answers (1)

You can do it:

type InnerFn =
  | (() => void)
  | ((arg: string) => void)
  | ((arg: number) => void)
  | ((arg: Record<string, unknown>) => void)

const main = <
  Params extends Parameters<InnerFn>,
  >(fn: (...params: Params) => void) =>
  (...args: Params) => fn(...args)

/**
 * Ok
 */
const result0 = main(() => { }) // ok
result0() // ok
const result1 = main((a: string) => { })// ok
result1('hello') // ok
const result2 = main((a: number) => { }) // ok
result2(42) // ok
const result3 = main((a: { age: number }) => { }) // ok
result3({ age: 42 })

/**
 * Errors
 */
const result4 = main((a: Promise<number>) => { }) // expected error
const result5 = main((a: number[]) => { }) // expected error

Playground

Explanation

Please dont use {} as a type because it is very general type. Everything is an object in javascript and typescript. Hence {} can't be used as a constraint. It is better to use Record<string, unknown> for such purpose.

Parameters - returns a tuple of all arguments of passed function.

P.S. I hope you don't mind that I have used arrow function instead of function. I'm not saying it is better, it just the matter of a style.

Upvotes: 1

Related Questions