I often find myself needing to do the following, and Flow makes it stupidly difficult:
/* @flow */
type Foo = {
+foo?: number,
}
type FooBar = {
+foo?: number,
+bar?: number,
}
const foo: Foo = {foo: 2}
function process(arg: $ReadOnly<FooBar>) {
}
process(foo)
Is there any good way to do this? I get the following error:
17: process(foo)
^ Cannot call `process` with `foo` bound to `arg` because property `bar` is missing in `Foo` [1] but exists in `FooBar` [2].
References:
12: const foo: Foo = {foo: 2}
^ [1]
14: function process(arg: $ReadOnly<FooBar>) {
^ [2]
Flow defaults to object types being inexact; this means that, although Foo
is only declared to explicitly have a single (optional) property foo
of type number
, a variable of type Foo
could theoretically have additional properties. For example, a variable of type Foo
could have a property bar
. And, since bar
is not typed in Foo
, bar
's type is unrestricted (i.e., not necessarily number
). As a result, if you were to interact with arg.bar
in process
and Flow allowed arg
to be of type Foo
, you are not guaranteed to be interacting with a number
. Thus, Flow complains.
One way to fix this is to use exact object types. If Flow knows that a variable of type Foo
will never have a bar
property, then it can safely be passed into process
. So, you could type Foo
as:
type Foo = {|
+foo?: number,
|};
(Note: the $ReadOnly
utility type is not necessary with the above definition of Foo
and the original definition of FooBar
because bar
is not writable. Though, certainly, it's fine to keep $ReadOnly
.)