【问题标题】:For a React + TypeScript variable: enforce some attributes in the "props" of a component对于 React + TypeScript 变量:在组件的“props”中强制执行一些属性
【发布时间】:2022-01-08 00:32:46
【问题描述】:

我想要一个变量(实际上是函数参数,但我简化了问题),它可以容纳必须具有的 React 组件,例如aNumber: number 在它的属性中。

我不成功的尝试是:

function MyComp(props: { aNumber: number, aString: string }) {
    return <>{props.aNumber} {props.aString}</>;
}

let f2: FunctionComponent<{ aNumber: number }> = MyComp;

给我错误:

Type '(props: { aNumber: number; aString: string; }) => Element' is not assignable to type 'FunctionComponent<{ aNumber: number; }>'.
  Types of parameters 'props' and 'props' are incompatible.
    Property 'aString' is missing in type 'PropsWithChildren<{ aNumber: number; }>' but required in type '{ aNumber: number; aString: string; }'.ts(2322)

FunctionComponent 在 react 中定义为:

    interface FunctionComponent<P = {}> {
        (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
        propTypes?: WeakValidationMap<P>;
        contextTypes?: ValidationMap<any>;
        defaultProps?: Partial<P>;
        displayName?: string;
    }

我不明白这个错误。如果我定义了一个带有约束的 var 以具有 aNumber,并且我为它分配了一个更丰富的元素 => 为什么它不开心?为什么它抱怨那些“更丰富”的属性?

当然,以下工作:

let f1: FunctionComponent<any> = MyComp;

通常,当我在 TS 中尝试高级打字构造时,我终于在经历了很多头痛之后让它工作了。这次好像不是很高级,但我没弄明白。

非常感谢。

【问题讨论】:

  • 这样定义函数:function MyComp(props: { aNumber: number, aString: string | undefined }) {return &lt;&gt;{props.aNumber} {props.aString}&lt;/&gt;;}

标签: reactjs typescript typescript-generics


【解决方案1】:

问题在于 React 组件是函数,而函数是相反变体的,这意味着如果需要带有 T 类型参数的函数,那么它可以满足更广泛类型的函数比T,但不是更窄的类型。这与诸如 co 变体的普通对象相反,即接受较窄的类型,但不接受较宽的类型。

你可以在没有任何 React 代码的情况下看到同样的东西

function MyFunc(props:{a: number, b: string}){return}

let func2: (props:{a:number})=>void = MyFunc 
//   ^-- error because MyFunc is narrower than typeof func2

一种直观的思考方式是从调用你的函数/React 组件的消费者的角度来考虑。在他们期望调用typeof f2 的函数的情况下,他们期望只需要传递aNumber 的道具,但是通过将更窄的函数分配给f2,他们现在还需要传递aString ,这是他们没有预料到的。

出于可以理解的原因,这个问题在 React 中经常出现。想象一下,你有一个 &lt;Button&gt; 组件,它接受一个 props,一个 &lt;FancyButton&gt; 组件接受多个 props。很容易想到,如果我需要任何旧的 &lt;Button&gt; 而不是 &lt;FancyButton&gt; 也可以。这听起来像是简单的继承。但是,如果您再考虑一下,就会出现错误。如果我是一个 component 期望需要插入一个 &lt;Button&gt; 作为我的孩子之一,这意味着我只希望需要传递它 one 道具。如果我现在换入&lt;FancyButton&gt;,那么该组件现在会期望得到一堆我没想到会提供的道具。

直观的一点是,您不只是显示&lt;Button&gt; 组件——在这种情况下,您可以不关心&lt;FancyButton&gt; 具有更多“花哨”这一事实。这是您实际上需要为组件提供输入--props--,因此更窄的输入将需要您(父组件)提供更多。

当然,同样的逻辑对于所有函数来说都是正确的,但我想它往往在 React 中出现得更频繁,因为我们倾向于将组件视为“要显示的对象”。

一般来说,这里有一个关于这个问题的更广泛的 SO 答案(起初很棘手!):Difference between covariant and contravariant positions in Typescript

【讨论】:

  • 另请参阅this。我不断地用有用的链接和this 用一些简单的例子来更新这个问题
  • @captain-yossarian,很好的链接。我在答案中添加了更多关于为什么(我认为)这会出现在 React 中以及如何直观地思考它的答案。我不确定付费专区链接上的 SO 政策是什么,所以不要添加到答案本身。
  • 它需要订阅(虽然你可以免费观看,我认为),但我发现这个解释(来自 Swift 程序员)非常有帮助。 pointfree.co/episodes/ep14-contravariance
  • 感谢您的链接!
  • 非常感谢您的回答,cmets,链接。我已经读了两遍了……这真的有点让人心碎。我的结论是,试图模仿我期望 &lt;Button/&gt; 而我收到 &lt;FancyButton/&gt; 的经典 OOP 实践实际上是不可行的。
猜你喜欢
  • 1970-01-01
  • 2019-10-16
  • 1970-01-01
  • 2018-08-08
  • 2022-01-11
  • 2021-05-10
  • 1970-01-01
  • 1970-01-01
  • 2017-11-05
相关资源
最近更新 更多