javascript - How can I spread props to a React component that uses exact props when using Flow? - Stack Overflow

Consider the following example using flow props.import * as React from 'react';type FooProps

Consider the following example using flow props.

import * as React from 'react';

type FooProps = {| 
  foo: number,
  bar?: string 
|};

class Foo extends React.Component<FooProps> {}

We have React ponent class Foo that accept an exact object of props. We want to enforce exactness so that users are not inadvertently making typos on their props (e.g. baz when they meant to use bar).

This works absolutely fine and errors happen as expected.

However, what if we want to spread props to this ponent from somewhere else? For example:

const f = (props: FooProps) => <Foo {...props} />;

Flow will give us an error about exactness on props:

10: const f = (props: FooProps) => <Foo {...props} />;
                                    ^ Cannot create `Foo` element because inexact props [1] is inpatible with exact `FooProps` [2].
References:
10: const f = (props: FooProps) => <Foo {...props} />;
                                   ^ [1]
8: class Foo extends React.Component<FooProps> {}
                                     ^ [2]

Disregarding the argument, "you shouldn't spread props to ponents like that when you're asking for exactness", how can spreading be achieved?

I did find one way to do this, but it uses an undocumented utility type $Shape<T> (code). It's unclear if this has any consequences or side effects, but it appears to work correctly:

class Foo extends React.Component<$Shape<FooProps>> {}

Here's a link to try out what I've got using $Shape<T> with items I expect to error (and not):

Consider the following example using flow props.

import * as React from 'react';

type FooProps = {| 
  foo: number,
  bar?: string 
|};

class Foo extends React.Component<FooProps> {}

We have React ponent class Foo that accept an exact object of props. We want to enforce exactness so that users are not inadvertently making typos on their props (e.g. baz when they meant to use bar).

This works absolutely fine and errors happen as expected.

However, what if we want to spread props to this ponent from somewhere else? For example:

const f = (props: FooProps) => <Foo {...props} />;

Flow will give us an error about exactness on props:

10: const f = (props: FooProps) => <Foo {...props} />;
                                    ^ Cannot create `Foo` element because inexact props [1] is inpatible with exact `FooProps` [2].
References:
10: const f = (props: FooProps) => <Foo {...props} />;
                                   ^ [1]
8: class Foo extends React.Component<FooProps> {}
                                     ^ [2]

Disregarding the argument, "you shouldn't spread props to ponents like that when you're asking for exactness", how can spreading be achieved?

I did find one way to do this, but it uses an undocumented utility type $Shape<T> (code). It's unclear if this has any consequences or side effects, but it appears to work correctly:

class Foo extends React.Component<$Shape<FooProps>> {}

Here's a link to try out what I've got using $Shape<T> with items I expect to error (and not):

Share Improve this question asked Feb 17, 2018 at 15:31 Paul ArmstrongPaul Armstrong 7,1861 gold badge25 silver badges36 bronze badges 2
  • Doesn't the $Shape variant just make the ponent take inexact props, whereas your function takes exact? So you've not solved the problem of making the ponent take exact props. My understanding was that its unwise to create ponents that accept exact props. Unreferenced props are not an issue. Mistyping prop names means that the expected prop value, when required, is missing and therefore will produce an error. – Dave Meehan Commented Feb 20, 2018 at 12:21
  • @DaveMeehan That's exactly what I mean by $Shape having unintended consequences. Mis-typed optional props can be problematic, given various use cases. That kind of error can be small, like just a missing visual cue, or large, like a loss of revenue. – Paul Armstrong Commented Feb 20, 2018 at 14:16
Add a ment  | 

2 Answers 2

Reset to default 7 +100

Apologies if this is not from an official source.

Solution: Type cast props to any before spread.

It meets your requirements of spread within a function. The function ensures exactness prior to type casting.

NB: $Shape<FooProps> on the cast does not work, it still thinks its exact. Neither does assigning props to a const spreadable: $Shape<FooProps> = props It seems $Shape doesn't remove the exactness, despite appearances in your example. One way or another, you have to strip the exactness, and doing it internally the function seems plausible.

The Flow docs discuss type casting via any as legitimate although potential unsafe and not remended (because you loose type safety). I think in the context its reasonable.

import * as React from 'react';

type FooProps = {| 
  foo: number,
  bar?: string 
|};

class Foo extends React.Component<FooProps> {}

const f = (props: FooProps) => <Foo {...(props: any)} />;

f({foo: 1})                     // PASS: with foo, without bar
f({foo: 1, bar: ''})            // PASS: with foo and bar
{ <Foo foo={1} /> }             // PASS: with foo, without bar
{ <Foo foo={1} bar="" /> }      // PASS: with foo and bar

f({})                           // FAIL: missing foo
f({foo: ''})                    // FAIL: wrong type of foo
f({foo: 1, bar: 1})             // FAIL: with foo, wrong type of bar
f({foo: 1, x: 1})               // FAIL: unexpected x
{ <Foo /> }                     // FAIL: missing foo
{ <Foo foo="" /> }              // FAIL: wrong type of foo
{ <Foo foo={1} bar={1} /> }     // FAIL: with foo, wrong type of bar
{ <Foo foo={1} x={1} /> }       // FAIL: unexpected x

Try it here

Looks like this is a very mon issue: Exact fails when doing es6 object spread

One option suggested throughout that thread is to use:

type Exact<T> = T & $Shape<T>

Then don't write your types as exact with {||}:

type FooProps = {
  foo: number,
  bar?: string 
}

class Foo extends React.Component<Exact<FooProps>> {}

This will allow you to use the spread operator and still get auto-plete of properties.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744390204a4571869.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信