Flow: Object spread properties not working as expected

Created on 8 Mar 2016  Â·  9Comments  Â·  Source: facebook/flow

Hey! I'm trying to work with some intersection types and using ES object spread syntax to update my data records. I'm not seeing the behaviour I would expect however.

In the following code I am seeing errors when I would expect it to work fine.

type Tri = boolean | null;

type Record = {
    id?: string,
    type: string
}

type Project = {
    type: 'project',
    accepted?: Tri
} & Record;

type PendingProject = {accepted: null} & Project;
type AcceptedProject = {accepted: true} & Project;
type RejectedProject = {accepted: false} & Project;

const pro: PendingProject = {
    type: 'project',
    id: '1',
    accepted: null
}

function accept(proj: PendingProject): AcceptedProject {
  return {...proj, accepted: true};
}

The errors I see are:

test.js:8
  8: type Project = {
                    ^ property `type`. Property not found in
 24:   return {...proj, accepted: true};
              ^^^^^^^^^^^^^^^^^^^^^^^^^ object literal

test.js:11
 11: } & Record;
         ^^^^^^ property `type`. Property not found in
 24:   return {...proj, accepted: true};
              ^^^^^^^^^^^^^^^^^^^^^^^^^ object literal

test.js:13
 13: type PendingProject = {accepted: null} & Project;
                                      ^^^^ null. This type is incompatible with
 14: type AcceptedProject = {accepted: true} & Project;
                                       ^^^^ boolean literal `true`

With my naivety I would expect flow to be able to see I am merging {accepted: true} with proj so it could safely assume that the result of that will satisfy PendingProject and {accepted: true} (AcceptedProject?)

Thanks in advance! :)

spread

Most helpful comment

Any news on this? Here is minimal repro:

    type A = { a: number, b: number }
    type B = { a: number, b: string }

    const a:A = { a:1, b:2 }
    const b:B = { ...a, b: 'should be ok but its not' }

All 9 comments

This doesn't have anything to do with intersections, but is instead a bug in how we process spreads.

Smaller repro without intersections:

type PendingProject = {
  type: 'project',
  accepted: null,
  id: string,
  type: string,
};

type AcceptedProject = {
  type: 'project',
  accepted: true,
  id: string,
  type: string,
};

const pro: PendingProject = {
  type: 'project',
  id: '1',
  accepted: null
}

function accept(proj: PendingProject): AcceptedProject {
  return {...proj, accepted: true};
}

Merging objects with Object.assign also seems to repro this.

Any news on this? Here is minimal repro:

    type A = { a: number, b: number }
    type B = { a: number, b: string }

    const a:A = { a:1, b:2 }
    const b:B = { ...a, b: 'should be ok but its not' }

Related: It seems that you cannot return a spread object typed as a disjoint union member, even when it (I think?) can only possibly return the correct value. For example, I would expect the following to pass flow checks:

type A = {
  name: 'FOO',
  values: Array<string>,
};

type B = {
  name: 'BAR',
  values: Array<string>,
};

function test<T: A | B>(arg: T): T {
  if (arg.name === 'BAR') {
    return {
      ...arg,
      values: [...arg.values, 'test'],
    };
  }
  return arg;
}

It instead fails with the following error:

15:     return {               ^ object literal. Could not decide which case to select
13: function test<T: A | B>(arg: T): T {
                     ^ union type

Demo here

^ That is not a solution. The problem still exists and I won't be using Flow until this is rectified.

Is there still no solution to this? Is there a workaround? Seems like a pretty big issue.

@rsolomo your type for test is not precise enough since it lets flow believe that you can send a A in and get a B out. Instead of A|B => A|B, using the more precise type A=>A | B=>B works:

const test2: A => A | B => B = (arg) => {
  if (arg.name === 'BAR') {
    return {
      ...arg,
      values: [...arg.values, 'test'],
    };
  }
  return arg;
}

Check out my comments on https://github.com/facebook/flow/issues/5253 to see why I believe this is actually correct behaviour for flow.
(I'm not flow dev, so my beliefs are only mine…)

This code no longer errors in master

Was this page helpful?
0 / 5 - 0 ratings

Related issues

StoneCypher picture StoneCypher  Â·  253Comments

jamesisaac picture jamesisaac  Â·  44Comments

vjpr picture vjpr  Â·  55Comments

STRML picture STRML  Â·  48Comments

gcanti picture gcanti  Â·  48Comments