Let's imagine you're creating a function which sums up an array of objects. Here's one, taken from the Excalidraw codebase:
Let's look at the type definition. This function takes in:
- An array of something:
readonly T[]
- A mapper function:
(item: T) => number
and returns number
.
In the body, it calls array.reduce(func, 0)
. This means the acc
in the function begins as 0
.
For each member of the array, it then adds acc
and mapper(item)
together. So, you end up with the sum of all of the members of the array.
What's the function declaration doing?
The mapper
function is the key. Let's strip it out to take a look at it:
Let's imagine a use case for this:
Here, mapper
represents the function that extracts the number from the object. The powerful thing about the sum
function is that you can discard most of these type declarations:
We've actually discarded all of the type declarations, but video
is still inferred as { name: string; views: number }
. This is possible because of the specificity of our function definition: (item: T) => number
.
What about Function
?
The big mistake I see a lot of beginner devs making is declaring a function like mapper
with the Function
type:
This keyword basically stands for 'any function'. It means that sum
can technically receive any function.
When used in sum
, we lose a lot of the safety that (item: T) => number
provided:
TypeScript now can't infer what item
is supposed to be, or what our mapper function is supposed to return.
The lesson here is 'don't use Function
' - there's always a more specific option available.
Alternatives
Expressing 'any function'
Sometimes, you'll want to express 'any function' in TypeScript. For this, let's look at some of TypeScript's built-in types, Parameters
and ReturnType
.
You'll notice that both of these utility types use the same constraint: (...args: any) => any
.
(...args: any)
specifies that the function can take any number of arguments, and => any
indicates that it can return anything.
Expressing a function with no args
For expressing a function with no arguments (but that returns anything), you'll want to use () => any
:
Summary
Function
should never be used when expressing types.
(a: string, b: number) => any
syntax can be used when you want to specify only the arguments, but not the return type.
(...args: any) => any
can be used to represent any function type.