The issue lies in declaring variables A
and B
with the type Fn
. This type only indicates that the arguments should be of type ...any[]
, causing any specific information about the assigned functions to be lost.
To retain type information, it is recommended to use a generic function for initializing the functions.
function createFunction<T extends Fn>(fn: T) { return fn }
We can restrict the passed function T
to be of type Fn
, while still returning a function of the original type T
.
By creating variables using this function, you will observe that the type information remains intact.
const a = createFunction(() => ['one', 1])
// const a: () => [string, number]
const b = createFunction((input: number) => ['two', input])
// const b: (input: number) => [string, number]
const c = createFunction((input: number) => ['two']) // Error: We broke the constraint!
Playground
Another approach could involve making Fn
generic. However, each time we use it to declare a function, we would need to explicitly provide the parameter type to Fn
.
type Fn<T extends any[] = void[]> = (...args: T) => ReturnFormat
const a: Fn = () => ['one', 1]
const b: Fn<[number]> = (input: number) => ['two', input]