Function Types

Typing function declarations and values

Functions have two places where types are applied: Parameters (input) and the return value (output).

1
2
3
4
5
6
7
8
// @flow
function concat(a: string, b: string): string {
  return a + b;
}

concat("foo", "bar"); // Works!
// $ExpectError
concat(true, false);  // Error!
boolean This type is incompatible with the expected param type of string boolean This type is incompatible with the expected param type of string

Using inference, these types are often optional:

1
2
3
4
5
6
7
8
// @flow
function concat(a, b) {
  return a + b;
}

concat("foo", "bar"); // Works!
// $ExpectError
concat(true, false);  // Error!
boolean This type cannot be added to string boolean This type cannot be added to string

Sometimes Flow’s inference will create types that are more permissive than you want them to be.

1
2
3
4
5
6
7
// @flow
function concat(a, b) {
  return a + b;
}

concat("foo", "bar"); // Works!
concat(1, 2);         // Works!

For that reason (and others), it’s useful to write types for important functions.

Syntax of functions

There are three forms of functions that each have their own slightly different syntax.

Function Declarations

Here you can see the syntax for function declarations with and without types added.

1
2
3
4
5
6
7
function method(str, bool, ...nums) {
  // ...
}

function method(str: string, bool?: boolean, ...nums: Array<number>): void {
  // ...
}

Arrow Functions

Here you can see the syntax for arrow functions with and without types added.

1
2
3
4
5
6
7
let method = (str, bool, ...nums) => {
  // ...
};

let method = (str: string, bool?: boolean, ...nums: Array<number>): void => {
  // ...
};

Function Types

Here you can see the syntax for writing types that are functions.

1
(str: string, bool?: boolean, ...nums: Array<number>) => void

You may also optionally leave out the parameter names.

1
(string, boolean | void, Array<number>) => void

You might use these functions types for something like a callback.

1
2
3
function method(callback: (error: Error | null, value: string | null) => void) {
  // ...
}

Function Parameters

Function parameters can have types by adding a colon : followed by the type after the name of the parameter.

1
2
3
function method(param1: string, param2: boolean) {
  // ...
}

Optional Parameters

You can also have optional parameters by adding a question mark ? after the name of the parameter and before the colon :.

1
2
3
function method(optionalValue?: string) {
  // ...
}

Optional parameters will accept missing, undefined, or matching types. But they will not accept null.

1
2
3
4
5
6
7
8
9
10
// @flow
function method(optionalValue?: string) {
  // ...
}

method();          // Works.
method(undefined); // Works.
method("string");  // Works.
// $ExpectError
method(null);      // Error!
null This type is incompatible with the expected param type of string

Rest Parameters

JavaScript also supports having rest parameters or parameters that collect an array of arguments at the end of a list of parameters. These have an elipsis ... before them.

You can also add type annotations for rest parameters using the same syntax but with an Array.

1
2
3
function method(...args: Array<number>) {
  // ...
}

You can pass as many arguments as you want into a rest parameter.

1
2
3
4
5
6
7
8
9
// @flow
function method(...args: Array<number>) {
  // ...
}

method();        // Works.
method(1);       // Works.
method(1, 2);    // Works.
method(1, 2, 3); // Works.

Note: If you add a type annotation to a rest parameter, it must always explicitly be an Array type.

Function Returns

Function returns can also add a type using a colon : followed by the type after the list of parameters.

1
2
3
function method(): number {
  // ...
}

Return types ensure that every branch of your function returns the same type. This prevents you from accidentally not returning a value under certain conditions.

1
2
3
4
5
6
7
// @flow
// $ExpectError
function method(): boolean {
  if (Math.random() > 0.5) {
    return true;
  }
}
boolean This type is incompatible with an implicitly-returned undefined.

Function this

Every function in JavaScript can be called with a special context named this. You can call a function with any context that you want.

In Flow you don’t type annotate this and Flow will check whatever context you call the function with.

1
2
3
4
5
6
7
function method() {
  return this;
}

var num: number = method.call(42);
// $ExpectError
var str: string = method.call(42);

Function Type

Sometimes it is useful to write types that accept arbitrary functions, for those you should write () => mixed like this:

1
2
3
function method(func: () => mixed) {
  // ...
}

However, if you need to opt-out of the type checker, and don’t want to go all the way to any, you can instead use Function. Function is unsafe and should be avoided.

For example, the following code will not report any errors:

1
2
3
4
5
6
7
8
9
function method(func: Function) {
  func(1, 2);     // Works.
  func("1", "2"); // Works.
  func({}, []);   // Works.
}

method(function(a: number, b: number) {
  // ...
});

You should follow all the same rules as any when using Function..


Was this guide helpful? Let us know by sending a message to @flowtype.