Flow: $Shape does not allow making properties read-only

Created on 3 Apr 2018  Â·  8Comments  Â·  Source: facebook/flow

When you have a type with a read-only property, you cannot convert any type that contains that property to the$Shape of that type.

Example: Try Flow

// @flow

type Foo = {
  +bar: "baz";
};

const f: Foo = { bar: "baz" };
const g: $Shape<Foo> = { bar: "baz" };

In this example, the declaration of f typechecks, but the declaration of g fails with the confusing error message, "Cannot assign object literal to g because property bar is not writable in property bar."

Most helpful comment

@phpnode That's great! I knew that the type spread operator is a way to turn an $Exact type into a regular inexact one, but didn't know that it would also make the props invariant.

Is that to say that $Shape doesn't have a bug and it's working as intended, or is it still a legit bug, and using the type spread operator is merely a workaround instead of The Wayâ„¢?

All 8 comments

Also having this issue. My use case is that I'd like to ensure that the props I'm returning from a mapStateToProps function are not wasted on the component:

import React, { PureComponent } from 'react';

type Props = {|
    +foo: boolean,
|};

class MyComponent extends PureComponent<Props> {
    static defaultProps = {
        foo: false,
    };

    render() {
        return (
            <div>{this.props.foo ? 'foo' : 'nofoo'}</div>
        );
    }
}

const mapStateToProps = (): $Shape<Props> => ({
    foo: true,
    bar: true,
});

Errors:

    19: const mapStateToProps = (): $Shape<Props> => ({
                                                      ^ Cannot return object literal because property `bar` is missing in object literal [1] but exists in `Props` [2].
        References:
        19: const mapStateToProps = (): $Shape<Props> => ({
                                                          ^ [1]
        19: const mapStateToProps = (): $Shape<Props> => ({
                                               ^ [2]
    19: const mapStateToProps = (): $Shape<Props> => ({
                                                      ^ Cannot return object literal because property `foo` is not writable in property `foo`.

Try Flow

I was only expecting the first error.

The type spread operator can help you here. For @CGamesPlay's scenario

// @flow

type Foo = {
  +bar: "baz";
};

const f: Foo = { bar: "baz" };

const g: $Shape<{...Foo}> = { bar: "baz" };

and in @zeorin's:

import React, { PureComponent } from 'react';

type Props = {|
    +foo: boolean,
|};

class MyComponent extends PureComponent<Props> {
    static defaultProps = {
        foo: false,
    };

    render() {
        return (
            <div>{this.props.foo ? 'foo' : 'nofoo'}</div>
        );
    }
}

const mapStateToProps = (): $Shape<{...Props}> => ({
    foo: true,
    bar: true,
});

@phpnode That's great! I knew that the type spread operator is a way to turn an $Exact type into a regular inexact one, but didn't know that it would also make the props invariant.

Is that to say that $Shape doesn't have a bug and it's working as intended, or is it still a legit bug, and using the type spread operator is merely a workaround instead of The Wayâ„¢?

Would someone from the FB team would be able to verify if this is a bug or flow working as expected?

Using the type spread operator is not really a solution, it just destroys "readonlyness" and after that $Shape works ok.

type Props = {|
    +foo: boolean,
|};
const value: $Shape<{...Props}> = { foo: true };
value.foo = false; // can write now without any error

Looks like this is fixed in 0.83.0.

Yup. Just came across this again, bumped to 0.83.0, error went away. This should be closed.

Thanks

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Macil picture Macil  Â·  47Comments

vjpr picture vjpr  Â·  55Comments

StoneCypher picture StoneCypher  Â·  253Comments

gcanti picture gcanti  Â·  48Comments

MarcoPolo picture MarcoPolo  Â·  67Comments