Flow: Cannot use union as a computed property in object spread

Created on 17 Feb 2020  路  2Comments  路  Source: facebook/flow

Hi. Since the big spread operator update, we are unable to upgrade flow because some of the patterns we use became unsupported. One of errors we can't just walk around is:
"Cannot use key [1] as a computed property. Computed properties may only be primitive literal values, but Day [2] is a union. Can you add a literal type annotation to key [1]? See https://flow.org/en/docs/types/literals/ for more information on literal types."

I've read https://medium.com/flow-type/spreads-common-errors-fixes-9701012e9d58 to find out why it is unsupported, yet neither the explanation nor the suggested workaround is satisfying.

Since we use immutable approach in our React app, we regularly use object spread to change some object field. The workaround suggest to mutate the field, which is not compliant with our code style. And we would have to clone each such object beforehand.

The reasoning behind the rule says that having 10,000-wide sting union leads to 10,000-wide object union. Well, I would say that having 10,000-wide string union is a bad idea in the first place, and 10,000-wide object union is just linearly worse. My suggested solution is to only throw error when the union is bigger then N, where N might as well be configurable in .flowconfig (This could also apply to https://github.com/facebook/flow/issues/8186). I see no reason why small unions should be discouraged.

To demonstrate some real-life-like scenario, I provide the example below. It represent a component with checkboxes for 7 days in a week, each with it's own boolean entry in the component's state.

Flow version: since 0.111.0

Expected behavior

It should be possible to use union as a computed property in object spread when the union is of reasonable size. It should be possible to type and immutably modify finite-size map-like object with known set of keys.

Actual behavior

Using union as a computed property in object spread is forbidden without any viable alternative.

bug needs triage

Most helpful comment

We're in the process of upgrading our codebase to v0.119 and finding this to be a real pain. To piggy back off @KlaraFickova's example above, we're having to change a bunch of these:

type Day = "Mon" | "Tue" | "Wen" | "Thu" | "Fri" | "Sat" | "Sun"

type Checked = {| [Day]: boolean |} 

function setChecked(x: Checked, key: Day): Checked {
  return {...x, [key]: true}
}

To this:

type Day = "Mon" | "Tue" | "Wen" | "Thu" | "Fri" | "Sat" | "Sun"

type Checked = {| [Day]: boolean |} 

function setChecked(x: Checked, key: Day): Checked {
  const next = {...x}
  next[key] = true
  return next
}

Some $ReadOnlys would make this safer, but it's still kind of a pain vs the previous behavior of allowing unions as keys or TypeScript's mapped types

All 2 comments

We're in the process of upgrading our codebase to v0.119 and finding this to be a real pain. To piggy back off @KlaraFickova's example above, we're having to change a bunch of these:

type Day = "Mon" | "Tue" | "Wen" | "Thu" | "Fri" | "Sat" | "Sun"

type Checked = {| [Day]: boolean |} 

function setChecked(x: Checked, key: Day): Checked {
  return {...x, [key]: true}
}

To this:

type Day = "Mon" | "Tue" | "Wen" | "Thu" | "Fri" | "Sat" | "Sun"

type Checked = {| [Day]: boolean |} 

function setChecked(x: Checked, key: Day): Checked {
  const next = {...x}
  next[key] = true
  return next
}

Some $ReadOnlys would make this safer, but it's still kind of a pain vs the previous behavior of allowing unions as keys or TypeScript's mapped types

@mcous thanks this worked.

Was this page helpful?
0 / 5 - 0 ratings