能够使用Flow根据另一个prop的存在或值来计算React组件的props吗?

huangapple go评论78阅读模式
英文:

Is it possible to compute type to react component props based on the presence or value of another prop using flow?

问题

我正在尝试根据另一个值来计算值的类型。如果 prop1 的类型是 x,那么 prop2 的类型应该是 y。

例如,在输入字段的情况下,

    <Input
     type="number"
     onChange={(value: number | string) => {}}
    />

如果 prop 的 type="number",那么 onChange 中的 value 应该是 number,否则应该是 string。我基本上试图在这里移除 | 条件。

当我尝试这样调用 Input 时:

    <Input
     type="number"
     onChange={(value: number) => {}}
    />

Flow 会抛出一个错误,因为如果使用 |,value 可以是数字或字符串。在这种情况下,类型显然是 &quot;number&quot;,有没有办法让 Flow 理解在这种情况下它将是数字?

Input 组件如下:

  type CommonProps = {
   disabled?: boolean,
   autoFocus?: boolean,
   placeholder: string
  };

  type PropsNumber = {
   type: 'number',
   onChange: (number) => void,
  };

  type PropsString = {
   type: 'string' | 'email',
   onChange: (string) => void,
  };

  type Props = CommonProps & (PropsNumber | PropsString);

  class Input extends React.Component<Props, {}> {
   render(){
     return (
        <input
          onChange={this.props.onChange}
          type={this.props.type}
          disabled={this.props.disabled}
          autoFocus={this.props.autoFocus}
        />
      );
    }
  }

在调用此组件的任何地方都会得到错误消息 Could not decide which case to select, since case 1 [1] may work but if it doesn't case 2 [2] looks promising too.。添加了一个playground以供参考。

英文:

I'm trying to compute the type of value based on another. If the type of prop1 is x then the type of prop2 should be y.

For example, in case of an input field,

    &lt;Input
     type=&quot;number&quot;
     onChange={(value: number | string) =&gt; {}}
    /&gt;

if the prop type=&quot;number&quot; then typeof value from onChange should be number and string otherwise. I'm basically trying to remove the | condition here.

When I try to call the Input like this:

    &lt;Input
     type=&quot;number&quot;
     onChange={(value: number) =&gt; {}}
    /&gt;

Flow is throwing an error because the value can be either number or string if the | is used. The value will obviously number in the case as the type is &quot;number&quot;. Is there a way to make flow understand this will be number in this case?

The Input component looks like:

  type CommonProps = {
   disabled?: boolean,
   autoFocus?: boolean,
   placeholder: string
  };

  type PropsNumber = {
   type: &#39;number&#39;,
   onChange: (number) =&gt; void,
  };

  type PropsString = {
   type: &#39;string&#39; | &#39;email&#39;,
   onChange: (string) =&gt; void,
  };

  type Props = CommonProps &amp; (PropsNumber | PropsString);

  class Input extends React.Component&lt;Props, {}&gt; {
   render(){
     return (
        &lt;input
          onChange={this.props.onChange}
          type={this.props.type}
          disabled={this.props.disabled}
          autoFocus={this.props.autoFocus}
        /&gt;
      );
    }
  }

Getting the error Could not decide which case to select, since case 1 [1] may work but if it doesn&#39;t case 2 [2] looks promising too. wherever this component is called.
Adding playground for the scenario.

答案1

得分: 2

解决这个问题的最佳方式是结合不交叉的联合和类型扩展来处理共同的属性:

import React from 'react';

type CommonProps = {
  autoFocus?: boolean,
  placeholder?: string,
};

type PropsNumber = {
  type: "number",
  onChange: (value: number) => void,
  ...CommonProps,
};

type PropsString = {
  type: "string",
  onChange: (value: string) => void,
  ...CommonProps,
};

type Props = PropsNumber | PropsString;

class Input extends React.Component<Props> {
  render() {
    if (this.props.type === "number") {
      this.props.onChange(5);
    } else {
      this.props.onChange("foo");
    }
    (this.props.autoFocus: ?boolean);
    // placeholder
    return null;
  }
}

<Input type="number" onChange={(value: number) => {}} />;

<Input type="string" onChange={(value: string) => {}} />;

// 预期的错误
<Input type="number" onChange={(value: string) => {}} />;

playground)

这段代码通过了Flow,除了在类型被指定为“number”但回调函数接受“string”类型参数时出现的预期错误。

不建议使用交叉类型来组合对象类型。相反,应该使用类型扩展,并且应该与精确类型一起使用。

请注意,在为了调用onChange而细化props的类型时,如果您做了比我这里的最小示例更复杂的事情,您可能会遇到细化失效。您可以通过将值提取到const绑定中来解决这些问题。

英文:

The best way to solve this is with a combination of disjoint unions and type spread for the common props:

import React from &#39;react&#39;;

type CommonProps = {|
 	autoFocus?: boolean,
  	placeholder?: string,
|};

type PropsNumber = {
  type: &quot;number&quot;,
  onChange: number =&gt; void,
  ...CommonProps,
};

type PropsString = {
  type: &quot;string&quot;,
  onChange: string =&gt; void,
  ...CommonProps,
};

type Props = PropsNumber | PropsString;

class Input extends React.Component&lt;Props&gt; {
  render() {
    if (this.props.type === &quot;number&quot;) {
      this.props.onChange(5);
    } else {
      this.props.onChange(&quot;foo&quot;);
    }
    (this.props.autoFocus: ?boolean);
    // placeholder
    return null;
  }
}

&lt;Input type=&quot;number&quot; onChange={(value: number) =&gt; {}}/&gt;;

&lt;Input type=&quot;string&quot; onChange={(value: string) =&gt; {}}/&gt;;

// Expected error 
&lt;Input type=&quot;number&quot; onChange={(value: string) =&gt; {}}/&gt;;

(playground)

This passes Flow, except for the expected error where the type is specified as number but the callback takes an argument of type string.

Intersection types are not recommended as a way to combine object types. Instead, type spread should be used, and it should be used with exact types.

Note that when you are refining the type of props in order to call onChange, if you do something more complicated than my minimal example here you may run into refinement invalidations. You'll be able to work around these by pulling values out into const bindings.

答案2

得分: -1

TypeScript的创建目的正是确定值的类型,如果它受到条件限制,它将失去其意义...

在您的情况下,您最多可以通过选择两种不同的函数来进行条件处理

onChange={
  type === "number" 
    ? (value: number) => {}
    : (value: string) => {}
}
英文:

type script was created exactly to determine the type of value, if it will be conditional it will lose its sense...

In your case the most you can do is condition with choice of two different functions

onChange={
  type === &quot;number&quot; 
    ? (value: number) =&gt; {}
    : (value: string) =&gt; {}
}

huangapple
  • 本文由 发表于 2020年1月6日 15:51:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/59608456.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定