Maybe Types
You can prefix a type with ?
to make it a union with null
and void
:
?T
is equivalent to the union T | null | void
.
For example, ?number
is equivalent to number | null | void
, and allows for numbers, null
, and undefined
as values. It's "maybe" a number.
1function acceptsMaybeNumber(value: ?number) {2 // ...3}4
5acceptsMaybeNumber(42); // Works!6acceptsMaybeNumber(); // Works! (implicitly undefined)7acceptsMaybeNumber(undefined); // Works!8acceptsMaybeNumber(null); // Works!9acceptsMaybeNumber("42"); // Error!
9:20-9:23: Cannot call `acceptsMaybeNumber` with `"42"` bound to `value` because string [1] is incompatible with number [2]. [incompatible-call]
In the case of objects, a missing property is not the same thing as an explicitly undefined
property.
1function acceptsMaybeProp({value}: {value: ?number}) {2 // ...3}4
5acceptsMaybeProp({value: undefined}); // Works!6acceptsMaybeProp({}); // Error!
6:18-6:19: Cannot call `acceptsMaybeProp` with object literal bound to the first parameter because property `value` is missing in object literal [1] but exists in object type [2]. [prop-missing]
If you want to allow missing properties, use optional property syntax, where the ?
is placed before the colon.
It is also possible to combine both syntaxes for an optional maybe type, for example {value?: ?number}
.
Refining maybe types
Imagine we have the type ?number
, if we want to use that value as a number
we'll need to first check that it is not null
or undefined
.
1function acceptsMaybeNumber(value: ?number): number {2 if (value !== null && value !== undefined) {3 return value * 2;4 }5 return 0;6}
You can simplify the two checks against null
and undefined
using a single
!= null
check which will do both.
1function acceptsMaybeNumber(value: ?number): number {2 if (value != null) {3 return value * 2;4 }5 return 0;6}
Most double equal checks are discouraged in JavaScript, but the above pattern is safe (it checks for exactly null
and undefined
).
You could also flip it around, and check to make sure that the value has a type
of number
before using it.
1function acceptsMaybeNumber(value: ?number): number {2 if (typeof value === 'number') {3 return value * 2;4 }5 return 0;6}
However, type refinements can be lost. For instance, calling a function after refining the type of an object's property will invalidate this refinement. Consult the refinement invalidations docs for more details, to understand why Flow works this way, and how you can avoid this common pitfall.