Skip to main content

Primitive Types

JavaScript has a number of different primitive types (MDN):

ExampleFlow type
Booleanstrue or falseboolean
Strings'foo'string
Numbers123number
Nullnullnull
Undefinedundefinedvoid
Symbols (new in ES2015)Symbol('foo')symbol
BigInts (new in ES2020)123nbigint

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 like true or false or the result of an expression like a === b.
  • A Boolean is a wrapper object created by the global new 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 like 42 or 3.14 or the result of an expression like parseFloat(x).
  • A Number is a wrapper object created by the global new 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 global new 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}