Functions
Functions can be typed at their parameters (input) and return value (output).
function concat(a: string, b: string): string {
return a + b;
}
concat("foo", "bar"); // Works!
concat(true, false); // Error! Return types can often be omitted thanks to inference, and parameter types can be inferred from context:
[1, 2, 3].map(x => x * x);
Syntax of functions
There are three forms of functions that each have their own slightly different syntax.
Function Declarations
function func(str: string, bool?: boolean, ...nums: Array<number>): void {
// ...
} Arrow Functions
let func = (str: string, bool?: boolean, ...nums: Array<number>): void => {
// ...
}; Function Types
type T = (str: string, bool?: boolean, ...nums: Array<number>) => void;
You may also optionally leave out the parameter names.
type T = (string, boolean | void, Array<number>) => void;
You might use these functions types for something like a callback.
function func(callback: (error: Error | null, value: string | null) => void) {
// ...
} Type arguments
Functions can have type arguments:
function f<T>(x: T): Array<T> {
return [x];
}
const g = <T>(x: T): Array<T> => [x];
type 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.
function func(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 :.
function func(optionalValue?: string) {
// ...
} Optional parameters will accept missing, undefined, or matching types. But they will not accept null.
function func(optionalValue?: string) {
// ...
}
func(); // Works.
func(undefined); // Works.
func("string"); // Works.
func(null); // Error! 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.
function func(...args: Array<number>) {
// ...
} You can pass as many arguments as you want into a rest parameter.
function func(...args: Array<number>) {
// ...
}
func(); // Works.
func(1); // Works.
func(1, 2); // Works.
func(1, 2, 3); // Works. 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:
type Counter = {
x: number,
getX: (this: Counter) => number,
};
function getX(this: Counter): number {
return this.x;
}
const counter: Counter = {x: 42, getX};
const num: number = counter.getX();
const str: string = counter.getX(); // Error! 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.
function func(): number {
return 1;
} 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.
function func(): boolean { // Error
if (Math.random() > 0.5) {
return true;
}
} Async functions implicitly return a promise, so the return type must always be a Promise.
async function func(): Promise<number> {
return 123;
} You can also use a special return type to declare that your function refines the type of a parameter. These are called type guards:
function isString(x: unknown): x is string {
return typeof x === 'string';
} Callable Objects
You can type functions with static properties as callable objects:
type CallableObj = {
(number, number): number,
bar: string,
...
};
function add(x: number, y: number) {
return x + y;
}
add.bar = "hello world";
add as CallableObj; Callable objects can also have multiple call signatures. A function assigned to this type must satisfy every signature:
type Formatter = {
(value: string): string,
(value: number): string,
...
};
const format: Formatter = (value: string | number): string => String(value);
const badFormat: Formatter = (value: number): string => String(value); // Error! 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:
type Callback = () => void;
const callback: Callback = () => {};
callback.description = "runs later"; // Error! Overloaded functions
You can use intersection types to define overloaded function types:
declare const fn:
& ((x: 'string') => string)
& ((x: 'number') => number)
const s: string = fn('string');
const 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:
function useCallback<T extends (...ReadonlyArray<empty>) => unknown>(
callback: T,
inputs: ?ReadonlyArray<unknown>,
): T {
return callback;
}
useCallback((x: string) => true); // OK
useCallback((x: number) => [1]); // OK You could use type arguments to capture the arguments and return type, to do more complicated transformations:
function func<TArgs extends ReadonlyArray<unknown>, TReturn>(
callback: (...TArgs) => TReturn,
): (boolean, ...TArgs) => Array<TReturn> {
return (b, ...args): Array<TReturn> => {
if (b) {
return [callback(...args)];
} else {
return [];
}
};
}
const f: (boolean, string, number) => Array<string> =
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.
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)
© 2013–present Facebook Inc.
Licensed under the MIT License.
https://flow.org/en/docs/types/functions