-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Exact fails when doing es6 object spread #2405
Comments
Yeah, I think your expected behavior is reasonable here. There are quite a few issues surrounding object spread (which is why it has its own issues tag), so we should probably take another look at the entire spread implementation with this and other issues in mind. Thanks for the report with simple repro code! |
@samwgoldman I'm curious if I should expect any activity on this issue and what a good workaround in the meantime might be. |
I discovered the same problem and noticed the same error applies when using type Foo = {
a: string,
b: string
};
const baseFoo: Foo = {
a: 'a',
b: 'b'
};
const extendedFoo: Foo = Object.assign(
{},
baseFoo,
{ a: 'c' }
); |
@pbomb, the work around I'm using is to not start making use of exact types until spread support improves. I rely heavily on object rest/spread so I can write pure functional JavaScript. OTOH, exact types seem like more of a nice to have feature. For example, there's a good chance my app will work after doing a large refactor once Flow's happy, but the tests may still fail if I have some extra properties left round that would cause any deep object comparisons to fail. It would be great if Flow caught that stuff too, but it's not the end of the world if it doesn't. At least that's how it's working out for me anyway... |
@pbomb A workaround we use:
The type |
@guicar 's solution works perfect for me! |
is there currently any activity on resolving this issue? |
As for custom Exact type, it breaks autocomplete in Atom. Just saying. |
This is quite a missing key feature when using
|
Just echoing @ferrannp. Ran into this in the same exact context (Redux reducers). Specifically, I have a function (reducer) that accepts a param of an exact object type (let's call it I'm attempting to do something like the following: const (anotherType: AnotherType) = { k: v };
// Copy a `SomeType` with one of the two `AnotherType` fields changed.
return ({
...(someType: SomeType),
attrTwo: anotherType,
}: SomeType) |
I have found this workaround to work quite well for our redux reducers: export type Foo = {
a: string,
b: string,
}
type Exact<T> = T & $Shape<T>;
function reducer(state: Foo = {}, action: any): Exact<Foo>{
return {
...state,
a: 'hello',
}
} Using the Thanks to @guicar for providing the Exact method. |
Is this indeed considered a bug? An entry in the docs right around "Exact objects" would be helpful! |
Ref facebook#2405, document limitation and workaround of exact object types with the spread operator.
Ref facebook#2405, document limitation and workaround of exact object types with the spread operator.
@guicar and @torarnek your workarounds do not include an exact object type, and so don't address this isssue. No errors:
Now make
Error: I am still struggling to find a way to clone an exact-typed object, without writing out every single one of its top level properties. |
@tuff Impossible with Flow 46. Don't use exact types. It's bummer, I know. |
@samwgoldman let us know if there is anything we can do to help on this... : ) Really missing this in redux, where you can misspell a state key. |
Agreed-- I would really like to defined my Redux state object as exact and be able to use the spread operator in my reducer, though Flow does not like that. I have not seen any reasonable work-arounds for this yet. |
Does this work in TypeScript? |
@skovhus -- I'm not sure if it works in TypeScript. I haven't tried.... |
To my knowledge, exact types are not expressable in typescript yet. |
this kind of exact type solve the problem with spread opertor. but it also create a new problem. this is a workground: type Obj = {
a: {
b: number
}
};
type ExactObj = Exact<Obj>;
const A: ExactObj = {
a: {
b: 1
}
};
let {
a: {
c
}
} = A; in this workground, no error will happen though c is not a key of type Obj and i think this new problem brings more hramness than can't use spread operator with exact type. |
I'm facing the same problem exporting actions like written below (with spreaded imports in Thanks
/* @flow */
type SearchChangeActionType = {
+type: string,
name: string,
value?: string,
};
export const searchInputChange =
({ name, value }: SearchInputType): SearchChangeActionType => {
return {
type: actionsTypes.SEARCH_INPUT_CHANGE,
name,
value,
};
};
/* @flow */
import * as formActions from './formActions';
import * as searchActions from './searchActions';
export default {
...formActions,
...searchActions,
}; In a component /* @flow */
import actions from '../../actions';
import type { DispatchType } from '../../types';
...
const mapDispatchToProps = (dispatch: DispatchType) => {
return {
onSearchChange: (value: string) => {
dispatch(actions.searchChange({ // ---------> NO ERROR if i replace the arg by a number
name: 'users',
value: 'Tony',
unWanted: 'foo', //----------------------> NO ERROR
}));
}
};
}; EditSwitching from import actions from '../../actions';
...
dispatch(actions.searchChange(...)); to import { searchChange } from '../../actions/searchChange';
...
dispatch(searchChange(...)); makes the flow typing work |
@MaxInMoon Try this export * from './formActions';
export * from './searchActions'; |
@TrySound thanks for the response. It's works perfecly but it force to import and use actions like so: import { myAction } from './actions'; But for actions I largely prefer: import actions from './actions';
// actions.myAction() |
@MaxInMoon And this import * as actions from './actions';
// actions.myAction() |
@TrySound 👍 thanks! Should we consider the use of |
It's how you should use imports. Using spread operator with module namespaces is probably not considered case in flow. In any case it's a hack and should be avoided since you have a better tool. |
Also, if you spread with an untyped value it disables the exact check. type Message = {| foo: number |}
function bar(): Message {
const opts = ({foo: 1}: any)
const message = {...opts, bar: 1}
return message
}
// No error.
|
@vjpr Can you try running the
|
@mandx Did not show any warnings. |
I noticed that the trick mentioned here seems to allow for spreading on an exact-ish type as well. |
It works... somewhat. However, we lose the type safety when we spread an inext object type to the "exact-ish type", and then all bets of which extraneous keys there are, is off. // @flow
// Type A is an "exact-ish object type"
type A = { a: string, [any]: empty };
// Type B is our good ol' inexact object type
type B = { b: string };
// Type C is also an "exact-ish object type"
type C = { c: string, [any]: empty };
declare var a: A;
declare var b: B;
declare var c: C;
const d = { ...a };
const e = { ...b };
const f = { ...a, ...b };
// This case has always worked
const g: { a: string, b: string } = { ...a, ...b };
// This case SHOULD trigger an error -- We can't guarantee that the
// non-exact object `b` does not have other keys besides `b` set
const h: { a: string, b: string, [any]: empty } = { ...a, ...b };
// This case should not trigger an error, as both `a` and `c` have
// defined with the `[any]: empty` hack that they don't have other
// keys besides `a` and `c` set to strings.
const i: { a: string, c: string, [any]: empty } = { ...a, ...c }; |
Another work-around for this (credit to #5551):
|
Any progress on this issue? |
It's happening: 1916b7d! If you switch version to master, the original example from the issue already shows no errors! 🎉 |
Super awesome! Unfortunately that is a bit too lax, though: // @flow
type A = {| a: string |};
type B = { b: string };
type C = {| c: string |};
declare var a: A;
declare var b: B;
declare var c: C;
var b: B = { b: 'foo', j: 'I am an extra property!' };
const d = { ...a };
const e = { ...b };
const f = { ...a, ...b };
const g: { a: string, b: string } = { ...a, ...b };
// This case SHOULD trigger an error, but it does not!!
const h: {| a: string, b: string |} = { ...a, ...b };
// This case should not trigger an error
const i: {| a: string, c: string |} = { ...a, ...c }; |
It does not work in JSX ( |
@AlexanderShushunov It should work in 0.71. |
This issue was getting out of hand. Might make sense to open new one, if such an issue doesn't already exist. If someone gets to it before me, it would be nice :) |
Ah, yeah, lets open a new one for that 👍 |
^ opened: #6425 |
I've opened #6854 to address the fact that this is still a problem with interfaces |
This fails:
With error:
This pattern is pretty common in redux style reducers where you return the old state merged with new values (e.g.
{...foo, foo: 4}
)The text was updated successfully, but these errors were encountered: