flowtype nullable object immutable property refinement

83 views Asked by At

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
}

Try flow

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?

2

There are 2 answers

0
Lyle Underwood On BEST ANSWER

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.

2
Krzysztof Krzeszewski On

You could try using indirect casting, where you cast the type onto any because any accepts any type of value as an input. Then because the any type also allows you to read all possible types from it, you can assign the any value to your type because an any is also your type.

/* @flow */
const a: {+foo: ?string} = {};

const fun = (obj: {+foo: string}) => {
  return obj
}

if (a.foo) {
  fun((a: any))
}