Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Object spread properties not working as expected #1511

Closed
chrisui opened this issue Mar 8, 2016 · 9 comments
Closed

Object spread properties not working as expected #1511

chrisui opened this issue Mar 8, 2016 · 9 comments

Comments

@chrisui
Copy link

chrisui commented Mar 8, 2016

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! :)

@avikchaudhuri
Copy link
Contributor

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};
}

@Zerim
Copy link

Zerim commented Jan 17, 2017

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

@MichalRemis
Copy link

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' }

@rsolomon
Copy link

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

@3stacks
Copy link

3stacks commented Jan 29, 2018

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

@benjyblack
Copy link

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

@Jym77
Copy link

Jym77 commented Nov 9, 2018

@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 #5253 to see why I believe this is actually correct behaviour for flow.
(I'm not flow dev, so my beliefs are only mine…)

@villesau
Copy link
Contributor

this fixes the issue: #7298

@nmote
Copy link
Contributor

nmote commented Oct 25, 2019

This code no longer errors in master

@nmote nmote closed this as completed Oct 25, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests