leontrolski
leontrolski

Reputation: 385

Is there a way to refer to function arguments in typescript definitions?

I have a {string: Function} map a:

const a: A = {
    foo: (x: string) => 8,
    bar: (y: number, z: boolean) => 6,
}

I then transform it such that every mapped function has a different type of return value:

const b: B = {
    foo: (x: string) => (8).toString(),
    bar: (y: number, z: boolean) => (6).toString(),
}

In TypeScript, is there any way to describe type B as derived from A, in my dream world, I'd like to be able to do:

type A = {
    foo: (string) => number
    bar: (number, boolean) => number
}
type B = {
    [K in keyof A]: (E in argsof A[K]) => string
}

Upvotes: 3

Views: 89

Answers (2)

tomshacham
tomshacham

Reputation: 41

You can achieve this using the built in Parameters<T> type as of Typescript 3.1:

type B = {
  [K in keyof A]:  (...a: Parameters<A[K]>) => string
}

This isn't explicitly documented in the Typescript docs on conditional types but similar conditional types are, like ReturnType<T>, you can see them all in the source.

With this in mind we could go a step further and relate B to the return type of our transforming function using ReturnType<T>:

const transformingFunction: (n: number) => string = (n: number) => n.toString();

type B = {
  [K in keyof A]:  (...a: Parameters<A[K]>) => ReturnType<typeof transformingFunction>
}

So now, if we want to change the return type of our transforming function, it can be done in one place, i.e. on the function itself without breaking the signature of B.

Upvotes: 3

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250196

Dreams come true in Typescript :)

You can achieve this in Typescript 3.0 using conditional types and Tuples in rest parameters and spread expressions:

type A = {
    foo: (s: string) => number
    bar: (n: number, b: boolean) => number
}


type ArgumentTypes<T extends (...a: any[]) => any> = T extends (...a: infer A) => any ? A : []; 
type B = {
    [K in keyof A]:  (...a:ArgumentTypes<A[K]>) => string 
}

let b: B;
b.foo("") // works
b.foo(1) // error

Upvotes: 5

Related Questions