Reputation: 4061
I'm looking for the one-argument typesafe equivalent of Function.prototype.call
in Typescript.
This does not work, because F
is missing the proper constraint (perhaps):
function call<F,A>(f: F, arg: A) {
return f(arg);
}
Thus, Typescript complains "This expression is not callable. Type 'unknown' has no call signatures."
How do I make F
callable? How do I manage call
to have the return type of F
?
Upvotes: 10
Views: 32468
Reputation: 5650
As you said, you'll need to add a constraint so it's callable. One option may look like the following:
function call<F extends Function,A>(f: F, arg: A) {
return f(arg);
}
Generic Constraints: https://www.typescriptlang.org/docs/handbook/generics.html#generic-constraints.
If you wanted to provide stronger type safety one approach may look like the following.
type Parameter<T> = T extends (arg: infer T) => any ? T : never;
function call<F extends (arg: any) => any>(f: F, arg: Parameter<F>): ReturnType<F> {
return f(arg);
}
const fn = (input: number): number => input * 2;
const result = call(fn, 2) // Valid
const result2 = call(fn, "2") // Argument of type '"2"' is not assignable to parameter of type 'number'.(2345)
const result3 = call(fn, false) // Argument of type 'false' is not assignable to parameter of type 'number'.(2345)
const badFn = (input: number, options: {}): number => input * 2;
const result4 = call(badFn, 2) // Argument of type '(input: number, options: {}) => number' is not assignable to parameter of type '(arg: any) => any'.(2345)
This defines a more strict constraint for F
, saying it must be a function that accepts only one argument. The argument is then inferred from that function. You could also use Parameters<F>[0]
since Parameters
is a utility type provided by Typescript. ReturnType
is another utility type to infer the return type of F
which can be used as the return type of call
.
Upvotes: 8