Home > OS >  How to narrow down types of a const
How to narrow down types of a const

Time:07-07

In the example below, I would like to mandate that all functions should return in a specific format [string, number], but input arguments for those functions can vary.

Playground link

type ReturnFormat = [string, number]
type Fn = (...args: any[]) => ReturnFormat

const A: Fn = () => ['one', 1]
const B: Fn = (input: number) => ['two', input]

type C = Parameters<typeof A>
type D = Parameters<typeof B>

However, when I try to read back the parameters of the function, both C and D are any[], and not null and input: number. What should I do to make that happen?

CodePudding user response:

The problem here is that you are declaring the variables A and B with the type Fn. The type Fn only knows that the arguments should be of type ...any[]. So when you use this type to declare variables, any specific information about the actual functions you used for the assignment is lost.

You should instead use a generic function to initialise the functions.

function createFunction<T extends Fn>(fn: T) { return fn }

We can constrain the passed function T to be of type Fn, but we still return a function of the original type T.

When we use this function to create the two variables, you will see that the type information is preserved.

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 constaint!

Playground


A second option would be to make Fn generic. But we would have to explicitely give the parameter type to Fn everytime we use it to declare a function.

type Fn<T extends any[] = void[]> = (...args: T) => ReturnFormat

const a: Fn = () => ['one', 1]
const b: Fn<[number]> = (input: number) => ['two', input]
  • Related