Primitive Types
JavaScript has a number of different primitive types (MDN):
Example | Flow type | |
---|---|---|
Booleans | true or false | boolean |
Strings | 'foo' | string |
Numbers | 123 | number |
Null | null | null |
Undefined | undefined | void |
Symbols (new in ES2015) | Symbol('foo') | symbol |
BigInts (new in ES2020) | 123n | bigint |
Some primitive types appear in the language as literal values:
1true;2"hello";33.14;4null;5undefined;63n;
BigInts and Symbols can be created with calls to BigInt
and Symbol
, respectively:
1BigInt("2364023476023");2Symbol("hello");
The Flow types of literal values are lowercase (mirroring the output of JavaScript's
typeof
expression):
1function func(a: number, b: string, c: boolean, d: bigint) { /* ... */ }2
3func(3.14, "hello", true, 3n);
Some literals can also be used as literal types:
1function acceptTwo(x: 2) { /* ... */ }2
3acceptTwo(2); // Works!4acceptTwo(1); // Error!
4:11-4:11: Cannot call `acceptTwo` with `1` bound to `x` because number [1] is incompatible with number literal `2` [2]. [incompatible-call]
Some primitives can also be wrapped as objects:
NOTE: You probably never want to use the wrapper object variants.
1new Boolean(false);2new String("world");3new Number(42);
Types for the wrapper objects are capitalized (the same as their constructor):
1function func(x: Number, y: String, z: Boolean) {2 // ...3}4
5func(new Number(42), new String("world"), new Boolean(false));
These wrapper objects are rarely used.
Booleans
Booleans are true
and false
values in JavaScript. The boolean
type in
Flow accepts these values.
1function acceptsBoolean(value: boolean) { /* ... */ }2
3acceptsBoolean(true); // Works!4acceptsBoolean(false); // Works!5
6acceptsBoolean("foo"); // Error!
6:16-6:20: Cannot call `acceptsBoolean` with `"foo"` bound to `value` because string [1] is incompatible with boolean [2]. [incompatible-call]
JavaScript can also implicitly convert other types of values into booleans.
if (42) {} // 42 => true
if ("") {} // "" => false
Flow understands these coercions and will allow them as part of an
if
statement's test or other conditional contexts.
To explicitly convert non-booleans to a boolean
, you can use Boolean(x)
or !!x
.
1function acceptsBoolean(value: boolean) { /* ... */ }2
3acceptsBoolean(0); // Error! 4
5acceptsBoolean(Boolean(0)); // Works!6acceptsBoolean(!!0); // Works!
3:16-3:16: Cannot call `acceptsBoolean` with `0` bound to `value` because number [1] is incompatible with boolean [2]. [incompatible-call]
You can refine a value to boolean
using a typeof
check:
1function acceptsBoolean(value: boolean) { /* ... */ }2
3function func(value: mixed) {4 if (typeof value === 'boolean') {5 acceptsBoolean(value); // Works: `value` is `boolean`6 }7}
Remember that boolean
and Boolean
are different types.
- A
boolean
is a literal value liketrue
orfalse
or the result of an expression likea === b
. - A
Boolean
is a wrapper object created by the globalnew Boolean(x)
constructor. You probably don't want to use this!
Numbers
Number literals in JavaScript are floating point numbers, for example 42
or 3.14
.
JavaScript also considers Infinity
and NaN
to be numbers.
These are represented by the number
type. JavaScript also has a separate BigInt type.
1function acceptsNumber(value: number) { /* ... */ }2
3acceptsNumber(42); // Works!4acceptsNumber(3.14); // Works!5acceptsNumber(NaN); // Works!6acceptsNumber(Infinity); // Works!7
8acceptsNumber("foo"); // Error! 9acceptsNumber(123n); // Error!
8:15-8:19: Cannot call `acceptsNumber` with `"foo"` bound to `value` because string [1] is incompatible with number [2]. [incompatible-call]9:15-9:18: Cannot call `acceptsNumber` with `123n` bound to `value` because bigint [1] is incompatible with number [2]. [incompatible-call]
You can refine a value to number
using a typeof
check:
1function acceptsNumber(value: number) { /* ... */ }2
3function func(value: mixed) {4 if (typeof value === 'number') {5 acceptsNumber(value); // Works: `value` is `number`6 }7}
Remember that number
and Number
are different types.
- A
number
is a literal value like42
or3.14
or the result of an expression likeparseFloat(x)
. - A
Number
is a wrapper object created by the globalnew Number(x)
constructor. You probably don't want to use this!
Strings
Strings are "foo"
values in JavaScript. The string
type in Flow accepts these values.
1function acceptsString(value: string) { /* ... */ }2
3acceptsString("foo"); // Works!4acceptsString(`template literal`); // Works!5
6acceptsString(false); // Error!
6:15-6:19: Cannot call `acceptsString` with `false` bound to `value` because boolean [1] is incompatible with string [2]. [incompatible-call]
JavaScript implicitly converts other types of values into strings by concatenating them.
"foo" + 42; // "foo42"
"foo" + {}; // "foo[object Object]"
Flow will only accept strings and numbers when concatenating them to strings.
1"foo" + "foo"; // Works!2"foo" + 42; // Works!3`foo ${42}`; // Works!4
5"foo" + {}; // Error! 6"foo" + []; // Error! 7`foo ${[]}`; // Error!
5:1-5:10: Cannot use operator `+` with operands string [1] and object literal [2] [unsafe-addition]6:1-6:10: Cannot use operator `+` with operands string [1] and empty array literal [2] [unsafe-addition]7:8-7:9: Cannot coerce array literal to string because empty array literal [1] should not be coerced. [incompatible-type]
You must be explicit and convert other types into strings. You can do this by using the String function or using another method for stringifying values.
1"foo" + String({}); // Works!2"foo" + [].toString(); // Works!3"" + JSON.stringify({}) // Works!
You can refine a value to string
using a typeof
check:
1function acceptsString(value: string) { /* ... */ }2
3function func(value: mixed) {4 if (typeof value === 'string') {5 acceptsString(value); // Works: `value` is `string`6 }7}
Remember that string
and String
are different types.
- A
string
is a literal value like"foo"
or the result of an expression like"" + 42
. - A
String
is a wrapper object created by the globalnew String(x)
constructor. You probably don't want to use this!
null
and undefined
JavaScript has both null
and undefined
. Flow treats these as separate
types: null
and void
(for undefined
).
1function acceptsNull(value: null) { /* ... */ }2
3acceptsNull(null); // Works!4acceptsNull(undefined); // Error! 5
6function acceptsUndefined(value: void) { /* ... */ }7
8acceptsUndefined(undefined); // Works!9acceptsUndefined(null); // Error!
4:13-4:21: Cannot call `acceptsNull` with `undefined` bound to `value` because undefined [1] is incompatible with null [2]. [incompatible-call]9:18-9:21: Cannot call `acceptsUndefined` with `null` bound to `value` because null [1] is incompatible with undefined [2]. [incompatible-call]
You can refine a value to null
or void
using equality checks:
1function acceptsNull(value: null) { /* ... */ }2
3function func(value: mixed) {4 if (value === null) {5 acceptsNull(value); // Works: `value` is `null`6 }7}
1function acceptsUndefined(value: void) { /* ... */ }2
3function func(value: mixed) {4 if (value === undefined) {5 acceptsUndefined(value); // Works: `value` is `void`6 }7}
null
and void
also appear in other types:
Maybe types
Maybe types are for places where a value is optional and you can create them by
adding a question mark in front of the type such as ?string
or ?number
.
?T
is equivalent to T | null | void
.
1function acceptsMaybeString(value: ?string) { /* ... */ }2
3acceptsMaybeString("bar"); // Works!4acceptsMaybeString(undefined); // Works!5acceptsMaybeString(null); // Works!6acceptsMaybeString(); // Works!
To refine, value == null
checks exactly for both null
and undefined
.
Read the maybe type docs for more details.
Optional object properties
Object types can have optional properties where a question mark ?
comes after
the property name.
{propertyName?: string}
In addition to their set value type, these optional properties can either be
void
or omitted altogether. However, they cannot be null
.
1function acceptsObject(value: {foo?: string}) { /* ... */ }2
3acceptsObject({foo: "bar"}); // Works!4acceptsObject({foo: undefined}); // Works!5acceptsObject({}); // Works!6
7acceptsObject({foo: null}); // Error!
7:21-7:24: Cannot call `acceptsObject` with object literal bound to `value` because null [1] is incompatible with string [2] in property `foo`. [incompatible-call]
Optional function parameters
Functions can have optional parameters where a question mark ?
comes after
the parameter name.
function func(param?: string) { /* ... */ }
In addition to their set type, these optional parameters can either be void
or omitted altogether. However, they cannot be null
.
1function acceptsOptionalString(value?: string) { /* ... */ }2
3acceptsOptionalString("bar"); // Works!4acceptsOptionalString(undefined); // Works!5acceptsOptionalString(); // Works!6
7acceptsOptionalString(null); // Error!
7:23-7:26: Cannot call `acceptsOptionalString` with `null` bound to `value` because null [1] is incompatible with string [2]. [incompatible-call]
Function parameters with defaults
Function parameters can also have defaults. This is a feature of ES2015.
function func(value: string = "default") { /* ... */ }
In addition to their set type, default parameters can also be void
or omitted
altogether. However, they cannot be null
.
1function acceptsOptionalString(value: string = "foo") { /* ... */ }2
3acceptsOptionalString("bar"); // Works!4acceptsOptionalString(undefined); // Works!5acceptsOptionalString(); // Works!6
7acceptsOptionalString(null); // Error!
7:23-7:26: Cannot call `acceptsOptionalString` with `null` bound to `value` because null [1] is incompatible with string [2]. [incompatible-call]
Symbols
Symbols are created with Symbol()
in JavaScript. Flow has basic support for symbols, using the symbol
type.
1function acceptsSymbol(value: symbol) { /* ... */ }2
3acceptsSymbol(Symbol()); // Works!4acceptsSymbol(Symbol.isConcatSpreadable); // Works!5
6acceptsSymbol(false); // Error!
6:15-6:19: Cannot call `acceptsSymbol` with `false` bound to `value` because boolean [1] is incompatible with symbol [2]. [incompatible-call]
You can refine a value to symbol
using a typeof
check:
1function acceptsSymbol(value: symbol) { /* ... */ }2
3function func(value: mixed) {4 if (typeof value === 'symbol') {5 acceptsSymbol(value); // Works: `value` is `symbol`6 }7}
BigInts
BigInts can be used to represent integers of arbitrary precision. In other words, they can store integers which are too large to store as a number
.
A bigint
literal is just a number
literal along with an n
suffix.
Note that bigint
and number
are incompatible types. That is, a bigint
cannot be used where a number
is expected, and vice versa.
1function acceptsBigInt(value: bigint) { /* ... */ }2
3acceptsBigInt(42n); // Works!4acceptsBigInt(42); // Error!
4:15-4:16: Cannot call `acceptsBigInt` with `42` bound to `value` because number [1] is incompatible with bigint [2]. [incompatible-call]
You can refine a value to bigint
using a typeof
check:
1function acceptsBigInt(value: bigint) { /* ... */ }2
3function func(value: mixed) {4 if (typeof value === 'bigint') {5 acceptsBigInt(value); // Works: `value` is `bigint`6 }7}