Type a Declared Function in TypeScript


Here's how to define a type for a declared function.

Declare a function

It's straightforward to type a function signature -- that is, its parameters and return value. For example, string in, number out:

function parseItBoom(input: string): number {
  return parseInt(input, 10);
}

Define a function type

But what if I want to type the function itself? Commonly, we rewrite as a variable binding:

type LeParser = (input: string) => number;

const parseItBoom: LeParser = (input) => {
  return parseInt(input, 10);
}

But this removes our a lovely declaration, and I like function declarations.

How will we get the typechecking for LeParser? We have to specify it at the point of usage. We cannot simply declare it and then attach a type and have TypeScript type-check it for us. Stylistically, it'd be nice to have the type check with a declaration only, but I don't think this is possible. But practically, no typing is needed until usage.

So, how do we type a function? Well, we can assign it, invoke it or refer to it.

Typecheck with assignment

This will typecheck:

const myParser: LeParser = parseItBoom;

Typecheck with invocation

This will typecheck:

const answer: number = parseItBoom("42")

Typecheck with reference

And this will typecheck:

function process(parse: LeParser) {
  // etc, (assignment and invocation)
}
process(parseItBoom)

Avoid casting

You might be tempted to cast the function to the type. But this isn't safe:

function parseItBoom(input: string): number {
  return parseInt(input, 10);
} as LeParser

We're forcing the compiler to accept the assertion of our type. If we changed this function implementation, the cast will prevent TypeScript from finding a new failure in usage.

Define function with a static member

Related to this topic of defining function types is the variation where the function has a static member. How do we define that?

If parseItBoom had a radix static number, we could define the type like this:

type LeParser = {
  (input: string): number
  radix: number
}

Where the implementation might look like this:

function parseItBoom(input: string): number {
  return parseInt(input, parseItBoom.radix
}
parseItBoom.radix = 10