Image for post
Image for post
Photo by Frame Harirak on Unsplash

Coming Soon: Changes to Object Spreads

Jordan Brown
Aug 20, 2019 · 10 min read

TL;DR

Object Spreads at Runtime

const obj1 = {foo: 3, bar: 3}; // Both foo and bar are own properties
obj1.hasOwnProperty('foo'); // true
obj1.hasOwnProperty('baz'); // false, baz is not a property on obj1
// foo and bar are accessible via obj2's prototype chain,
// but they do not belong to obj2 itself
const obj2 = Object.create(obj1);
obj2.foo === 3; // true, we look at obj2's prototype to access foo
obj2.hasOwnProperty('foo'); // false, foo does not belong
// directly to obj2
const obj1 = {foo: 3, bar: 3}; // foo and bar are both own
const obj2 = {...obj1};
obj1.foo === obj2.foo; // true
obj1.bar === obj2.bar; // true
const obj3 = Object.create({foo: 3});;
obj3.bar = 3;
const obj4 = {...obj3}obj4.foo === obj3.foo; // false, foo is not copied because it is not own
obj4.bar === obj3.bar; // true, bar is copied to obj4 because it is own
const obj1 = {foo: 3, ...{foo: 4}};
obj1.foo === 4; // true
const obj2 = {foo: 3, ...{foo: 4}, foo: 5};
obj2.foo === 5; // true
const obj3 = {foo: 3, ...{bar: 3}, ...{foo: 4, bar: 4}};
obj3.foo === 4; // true
obj3.bar === 4; // true

Flow’s Current Model for Type Spreads

declare var a: A;
declare var b: B;
const o: {...A, ...B} = {...a, ...b};
const o1: {| foo: number |} = {foo: 3}; // Ok!
const o2: {| foo: number |} = Object.create({foo: 3}); // Error
const o1: {foo: number} = {foo: 3}; // Ok!
const o2: {foo: number} = {foo: 3, bar: 3}; // Ok!
const o3: {foo: number} = Object.create({foo: 3}); // Ok!
type OtherProps = {|
buttonText: string,
|};
type Props = {|
...OtherProps,
headerText: string,
|};
class Banner extends React.Component<Props> {
// ...
}
type Props = {|
buttonText: string,
headerText: string,
|};
type ButtonProps = {
borderShade: number,
};
type Props = {
...ButtonProps,
borderWidth?: number,
color: number,
};
class FormButton extends React.Component<Props> {
// ...
}
// Valid, since all of the properties in Props except color are optional
const intantiation1 = <FormButton color={3} />;
// All ok, since every property is optional except color.
const props1: Props = {borderShade: 3, color: 3};
const props2: Props = {borderWidth: 3, color: 3};
const props3: Props = {color: 3};
class FormButton extends React.Component<Props> {
render(): React.Node {
return <Button color={3} borderShade={this.props.borderShade} />; // Error borderShade might be undefined
}
}
const props: Props = {borderShade: 3, color: 3};(props.color: number); // Ok
(props.borderShade: number); // Error, borderShade might be undefined
type ButtonProps = {
borderShade: number,
};
type InjectedProps = {
transparency: number,
};
type Props = {
...ButtonProps,
borderWidth?: number,
color: number,
...InjectedProps,
};
class FormButton extends React.Component<Props> {
// ...
}
const intantiation = <FormButton
color="Not a number!"
/>; // No error, only color is required, and color is typed mixed!
const props: Props = {color: 'string'}; // Ok!

The New Spread Model

const o1: {| foo: number |} = {foo: 3}; // Ok!
const o2: {| foo: number |} = Object.create({foo: 3}); // Error
const o1: { foo: number } = {foo: 3}; // Ok!
const o2: { foo: number } = {foo: 3, bar: 3}; // Ok!
const o3: { foo: number } = Object.create({foo: 3}); // Not ok, foo is not own
interface I { foo: number}
const o1: I = {foo: 3}; // Ok!
const o2: I = {foo: 3, bar: 3}; // Ok!
const o3: I = Object.create({foo: 3}); // Ok!
type ButtonProps = {
borderShade: number,
};
type Props = {
...ButtonProps,
borderWidth?: number,
color: number,
};
class FormButton extends React.Component<Props> {
// ...
}
// Error, borderShade and color are missing!
const intantiation = <FormButton />;
const props: Props = {borderShade: 3, color: 3}; // Ok, all required properties are included
class FormButton extends React.Component<Props> {
render(): React.Node {
// Ok! props.borderShade has the expected type
return <Button borderShade={this.props.borderShade} ... />;
}
}
type ButtonProps = {
borderShade: number,
};
type InjectedProps = {
transparency: number,
};
type Props = {
...ButtonProps,
borderWidth?: number,
color: number,
// Error, InjectedProps is inexact, so borderWidth may be overwritten
...InjectedProps,
};
class FormButton extends React.Component<Props> {
// ...
}
Cannot determine a type for Props [1]. InjectedProps [2] is inexact, so it may contain `color` with a type that
conflicts with `color`'s definition in Props [1]. Can you make InjectedProps [2] exact?
[1] 12│ type Props = {
13│ ...ButtonProps,
14│ borderWidth?: number,
15│ color: number,
[2] 16│ ...InjectedProps,
17│ };
18│
19│ class FormButton extends React.Component<Props> {

Conclusion

Flow

The official publication for the Flow static type checker…

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store