Skip to main content

Functions

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

1function concat(a: string, b: string): string {2  return a + b;3}4
5concat("foo", "bar"); // Works!6concat(true, false);  // Error!incompatible-typeCannot call concat with true bound to a because boolean [1] is incompatible with string [2].incompatible-typeCannot call concat with false bound to b because boolean [1] is incompatible with string [2].

Using inference, return types are often optional:

1function concat(a: string, b: string) {2  return a + b;3}4
5const s: string = concat("foo", "bar"); // Works!

If defined where we can get the type from the context of the expression, type annotations can be optional:

1[1, 2, 3].map(x => x * x); // From the context, we know parameter `x` has type `number`

Syntax of functions

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

Function Declarations

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

Arrow Functions

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

Function Types

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

You may also optionally leave out the parameter names.

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

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

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

Type arguments

Functions can have type arguments:

1function f<T>(x: T): Array<T> {2  return [x];3}4
5const g = <T>(x: T): Array<T> => [x];6
7type H = <T>(T) => Array<T>;

Function Parameters

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

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

Optional Parameters

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

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

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

1function func(optionalValue?: string) {2  // ...3}4
5func();          // Works.6func(undefined); // Works.7func("string");  // Works.8
9func(null);      // Error!incompatible-typeCannot call func with null bound to optionalValue because null [1] is incompatible with string [2].

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 ellipsis ... before them.

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

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

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

1function func(...args: Array<number>) {2  // ...3}4
5func();        // Works.6func(1);       // Works.7func(1, 2);    // Works.8func(1, 2, 3); // Works.

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

this parameter

Every function in JavaScript can be called with a special context named this. You can call a function with any context that you want. Flow allows you to annotate the type for this context by adding a special parameter at the start of the function's parameter list:

1function func<T>(this: { x: T }) : T {2  return this.x;3}4
5const num: number = func.call({x : 42});6const str: string = func.call({x : 42}); // Error!incompatible-typeCannot assign func.call(...) to str because number [1] is incompatible with string [2].

This parameter has no effect at runtime, and is erased along with types when Flow is transformed into JavaScript. When present, this parameters must always appear at the very beginning of the function's parameter list, and must have an annotation. Additionally, arrow functions may not have a this parameter annotation, as these functions bind their this parameter at the definition site, rather than the call site.

If an explicit this parameter is not provided, Flow will attempt to infer one based on usage. If this is not mentioned in the body of the function, Flow will infer unknown for its this parameter.

Function Returns

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

1function func(): number {2  return 1;3}

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.

1function func(): boolean {incompatible-typeCannot expect boolean as the return type of function because boolean [1] is incompatible with implicitly-returned undefined.2  if (Math.random() > 0.5) {3    return true;4  }5}

Async functions implicitly return a promise, so the return type must always be a Promise.

1async function func(): Promise<number> {2  return 123;3}

You can also use a special return type to declare that your function refines the type of a parameter. These are called type guards:

1function isString(x: mixed): x is string {2  return typeof x === 'string';3}

Callable Objects

Callable objects can be typed, for example:

1type CallableObj = {2  (number, number): number,3  bar: string,4  ...5};6
7function add(x: number, y: number) {8  return x + y;9}10
11add.bar = "hello world";12
13add as CallableObj;

In general, functions can have properties assigned to them if they are function declarations, or simple variable declarations of the form const f = () => .... The properties must be assigned in the format f.prop = <expr>;, in the same statement list as the function definition (i.e. not conditionally).

Note that the object representing the static properties assigned to the function is inexact.

Overloaded functions

You can use intersection types to define overloaded function types:

1declare const fn:2  & ((x: 'string') => string)3  & ((x: 'number') => number)4
5const s: string = fn('string');6const n: number = fn('number');

Any function

If you want to specify you want to allow any function, and do not care what it is, you can use this pattern:

1function useCallback<T: (...ReadonlyArray<empty>) => unknown>(2  callback: T,3  inputs: ?ReadonlyArray<unknown>,4): T {5  return callback;6}7useCallback((x: string) => true); // OK8useCallback((x: number) => [1]); // OK

You could use type arguments to capture the arguments and return type, to do more complicated transformations:

1function func<TArgs: ReadonlyArray<unknown>, TReturn>(2  callback: (...TArgs) => TReturn,3): (boolean, ...TArgs) => Array<TReturn> {4  return (b, ...args): Array<TReturn> => {5    if (b) {6      return [callback(...args)];7    } else {8      return [];9    }10  };11}12
13const f: (boolean, string, number) => Array<string> =14  func((x: string, y: number) => x.slice(y)); // OK

The type Function is just an alias for any, and is unsafe. You can ban its use in your code with the unclear-type lint.