Skip to main content

Functions

Functions can be typed at their parameters (input) and 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 true [1] is incompatible with string [2].incompatible-typeCannot call concat with false bound to b because false [1] is incompatible with string [2].
Try

Return types can often be omitted thanks to inference, and parameter types can be inferred from context:

1[1, 2, 3].map(x => x * x);
Try

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}
Try

Arrow Functions​

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

Function Types​

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

You may also optionally leave out the parameter names.

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

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

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

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>;
Try

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}
Try

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}
Try

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].
Try

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}
Try

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.
Try

Note: If you add a type annotation to a rest parameter, it must be an array-like type (e.g., Array<T>, ReadonlyArray<T>, or a tuple type).

this parameter​

Every function in JavaScript is called with a special context named this. Flow allows you to annotate the type for this context by adding a special parameter at the start of the function's parameter list:

1type Counter = {2  x: number,3  getX: (this: Counter) => number,4};5
6function getX(this: Counter): number {7  return this.x;8}9
10const counter: Counter = {x: 42, getX};11const num: number = counter.getX();12const str: string = counter.getX(); // Error!incompatible-typeCannot assign counter.getX() to str because number [1] is incompatible with string [2].
Try

The context comes from the receiver of the call. For receiver-specific methods, call, apply and bind may only re-supply that same receiver — see Rebinding this.

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}
Try

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 { // Errorincompatible-typeCannot expect boolean as the return type of function because boolean [1] is incompatible with void [1].2  if (Math.random() > 0.5) {3    return true;4  }5}
Try

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

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

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: unknown): x is string {2  return typeof x === 'string';3}
Try

Constructor Types ≥0.339​

A constructor type new (...) => T describes a value that you can call with new to produce T. Use it to accept a class constructor in a function, such as this generic factory:

1function create<T>(C: new () => T): T {2  return new C();3}4
5class Item {6  value: number = 1;7}8
9create(Item).value as number; // OK
Try

Unlike a function type (...) => T, a constructor type describes construction, not an ordinary function call. It is a type annotation, not syntax for declaring a runtime function. For the distinction between an instance type and the class value, see Classes in annotations.

Flow checks constructor arguments when you use new. If a constructor requires an argument, you cannot assign it to a zero-argument constructor type:

1class Item {2  name: string;3  constructor(name: string) {4    this.name = name;5  }6}7
8const C: new (name: string) => Item = Item; // OK9new C('book').name as string; // OK10new C(123); // Error! `name` must be a stringincompatible-typeCannot call C with 123 bound to name because 123 [1] is incompatible with string [2].11new C(); // Error! `name` is requiredincompatible-typeCannot call C because (name: string) => Item [1] requires another argument.12const noArgs: new () => Item = Item; // Error! `Item` requires an argumentincompatible-typeCannot assign Item to noArgs because (name: string) => Item [1] requires another argument from function type [2].
Try

You can capture the parameter list with a tuple type to write a variadic factory:

1function instantiate<T, Args extends ReadonlyArray<unknown>>(2  C: new (...args: Args) => T,3  ...args: Args4): T {5  return new C(...args);6}7
8class Point {9  x: number;10  label: string;11  constructor(x: number, label: string) {12    this.x = x;13    this.label = label;14  }15}16
17instantiate(Point, 1, 'start').x as number; // OK18instantiate(Point, 'bad', 'start'); // Error! The first argument must be a numberincompatible-typeCannot call instantiate with Point bound to C because in the first argument: string [1] is incompatible with number [2].
Try

When you use a class as the result type, Flow preserves its nominal class identity. To accept different classes that share a structural contract, use an interface as the result type rather than a plain object type:

1class Item {2  value: number = 1;3}4class OtherItem {5  value: number = 2;6}7
8const specific: new () => Item = Item; // OK9const wrong: new () => Item = OtherItem; // Error! Different class identityincompatible-typeCannot assign OtherItem to wrong because in the return value: OtherItem [1] is incompatible with Item [2].10
11interface WithValue {12  value: number;13}14const structural: new () => WithValue = OtherItem; // OK15new structural().value as number; // OK
Try

In ordinary Flow source, {new(): T} describes an object method named new, not TypeScript's object construct signature. An interface can instead declare a construct signature:

1declare const methods: {new(): {value: number}};2methods.new().value as number; // OK: method call3new methods(); // Error! Not a constructorinvalid-constructorCannot use new on {new(): {value: number}} [1]. Only classes and interfaces with a construct signature can be constructed.4
5interface ItemConstructor {6  new(): {value: number};7}8declare const C: ItemConstructor;9new C().value as number; // OK: construction
Try

ConstructorParameters<C> extracts the constructor's parameter tuple. To extract its result type, use InstanceType<C>. Both utilities accept constructor types.

Abstract constructor types​

Use abstract new (...) => T when a constructor value may be an abstract class. This type accepts both abstract and concrete constructors with compatible parameters and results. You cannot construct an instance through it, even if the underlying class is concrete:

1abstract class Animal {2  abstract makeSound(): string;3}4class Dog extends Animal {5  makeSound(): string {6    return 'woof';7  }8}9
10const base = Animal as abstract new () => Animal; // OK11const pet = Dog as abstract new () => Animal; // OK12new base(); // Error! Abstract constructor typeinvalid-abstractCannot instantiate an abstract class.13new pet(); // Error! Still an abstract constructor typeinvalid-abstractCannot instantiate an abstract class.14
15Animal as new () => Animal; // Error! Abstract class in a concrete constructor slotinvalid-abstractCannot assign an abstract constructor type to a non-abstract constructor type.16const concrete = Dog as new () => Animal; // OK17new concrete().makeSound() as string; // OK
Try

Adoption​

To use this syntax, set experimental.tslib_syntax=true in .flowconfig and use Flow and flow-parser 0.339.0+ and Prettier 3.10.0+ with its built-in flow parser.

Callable Objects​

You can type functions with static properties as callable objects:

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;
Try

Callable objects can also have multiple call signatures. A function assigned to this type must satisfy every signature:

1type Formatter = {2  (value: string): string,3  (value: number): string,4  ...5};6
7const format: Formatter = (value: string | number): string => String(value);8
9const badFormat: Formatter = (value: number): string => String(value); // Error!incompatible-typeCannot assign function to badFormat because in the first parameter: number [1] is incompatible with string [2].
Try

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.

When you annotate a variable with a plain function type, Flow does not infer extra static properties:

1type Callback = () => void;2
3const callback: Callback = () => {};4
5callback.description = "runs later"; // Error!prop-missingCannot assign "runs later" to callback.description because property description is missing in {...} [1].
Try

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');
Try

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 extends (...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
Try

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

1function func<TArgs extends 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
Try

The type Function refers to the global Function class. It exposes function properties such as name and length, but has no call signature. Use a function type with explicit parameters and a return type when you need to call the function.

See Also​

  • Generics — parameterized types used with functions for type-safe reusable code
  • Type Guards — special return types that refine parameter types
  • Intersections — defining overloaded function types
  • Subtypes — how function subtyping works (contravariant parameters, covariant returns)