I want to use refinement for a property that can be null. This object with the checked property then passed to function as an argument.
/* @flow */
const a: {+foo: ?string} = {};
const fun = (obj: {+foo: string}) => {
return obj
}
if (a.foo) {
fun(a) // null or undefined [1] is incompatible with string
}
It shouldn't work with an object with mutable property, because this property can be changed to null
later. So that's why I use the immutable property. But it still doesn't work.
Is there a way to pass object with the refined property?
Refining a property of an object refines the property, not the object.
// `a.foo` is of type `?string`
// `a` is of type `{+foo: ?string}`
if (a.foo) {
// within this block, `a.foo` is of type `string` (no `?`)
// `a` is of type `{+foo: ?string}`
}
// `a.foo` is of type `?string`
// `a` is of type `{+foo: ?string}`
In this particular case, I would probably do something like this:
if (a.foo) {
fun({ foo: a.foo });
}
(Try)
Just because it's such a simple case. In a more complex case, you'd want to use disjoint unions.
type A = {
+foo: string,
};
type B = {
+foo: void,
};
type T = A | B;
const a: T = ({ foo: undefined }: B);
const fun = (obj: A) => {
return obj
}
// `a` is of type `A | B` (which is type `T`)
if (a.foo) {
// inside this block `a` is of type `A`
fun(a);
}
// `a` is of type `A | B` (which is type `T`)
(Try)
At the end of the day, there is no super direct way to convert { +foo: ?string }
to { +foo: string }
because they are two completely different complex types, and must be handled as such.