I'm curious as to how to correctly type function parameters, regarding generics.
Looking at the following basic example, I have 2 functions:
- sayHello
& runAnyFunction
Now, the first call to sayHello
is clearly an error, the data passed to the function doesn't have a name
parameter. Perfect and expected.
The issue is with the next part, where we have a function runAnyFunction
that takes a function as its first parameter, obj
as its second parameter, and simply runs the function passing obj
as the argument.
The following 2 functions in the sample below will pass typescript checks no problem, and that is my issue.
runAnyFunction<Person>(sayHello, {
name: "Bob"
});
runAnyFunction<Monster>(sayHello, {
size: "HUGE"
});
I know that the second call will give me a Hello undefined
, which is undesired. Ideally I'd like this call to fail type checking as this shouldn't be allowed.
My thinking is that the culprit here is the fn
function parameter, as its own obj
parameter will be inferred as any
, but I'm having difficulty writing the correct typing for this.
I understand that in this demo, my runAnyFunction
is a function that really just runs any function, but if there's a preferred way to type a function of this nature, I would love to know.
How can I correctly type this?
Code Sample
// demo.ts
type Fn = (obj) => string;
interface Person {
name: string;
}
interface Monster {
size: string;
}
const somePerson: Person = {
name: "Bob"
};
const someMonster: Monster = {
size: "HUGE"
};
sayHello(someMonster); // ERROR, 'name' is missing in type. Sure, expected.
// sayHello requires an obj of type Person
export function sayHello(obj: Person): string {
return `Hello ${obj.name}`;
}
// Simple function that runs any function, passing obj to it
export function runAnyFunction<T>(fn: Fn, obj: T): void {
const res = fn(obj);
console.log(res);
}
// Person has name, this works
runAnyFunction<Person>(sayHello, {
name: "Bob"
});
// I'm passing sayHello, which should require an object with a `name`
// but it does not error
runAnyFunction<Monster>(sayHello, {
size: "HUGE"
});
You need to make your Fn
type generic too:
type Fn<T> = (obj: T) => string;
export function runAnyFunction<T>(fn: Fn<T>, obj: T): void {
const res = fn(obj);
console.log(res);
}
The reason that you didn't receive an error before is that your Fn
type implicity had the any
type for its first parameter:
type Fn = (obj) => string; // obj is implicitly of type any
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With