【问题标题】:Typescript: Pass component given props to the component's children prop打字稿:将组件给定的道具传递给组件的子道具
【发布时间】:2021-11-10 09:27:12
【问题描述】:

我想要实现的是创建通用类型的组件,它可以接受不同的道具(基于它在项目中的使用),然后最终输出它的子道具和所有给定的道具(流通)+可能是一些新的常量(将根据道具计算),如下所示:

const bonus1 = { offerId: 1, offerDate: 1911 }
const bonus2 = { offerId: 2, timeLeft: 100 }
const bonus3 = { offerId: 3, offerDate: 1991, isActive: true }

<Bonus {...bonus1}>
{({ offerId, offerDate, onClick }) => {})
</Bonus>

<Bonus {...bonus2}>
{({ offerId, timeLeft, onClick }) => {})
</Bonus>

<Bonus {...bonus3}>
{({ offerId, offerDate, isActive, onClick }) => {})
</Bonus>

我已经准备了一些常见的类型,其中包含我知道每个 bonus 都会有的字段,例如:offerId

type BonusCommon = { offerId: number }

现在组件类型:

type BonusProps<T> = {
  children: (renderProps: RenderProps<T>) => ReactNode
} & T

type RenderProps<T> = {
  onClick: () => void
} & T // here is what I thought would work, ie. pass to `children` all the input props

还有组件本身:

const Bonus = <T extends BonusCommon>({
  children,
  offerId,
  ...rest, // the rest of the input props
}: BonusProps<T>) => {
  const onClick = () => {} // do something with available props

  return (
    <>
      children({ // TS ERROR!
        onClick,
        offerId,
        ...rest
      })
    </>
  )
}

我没有真正使它起作用的想法,因为目前有一个错误说:'T' could be instantiated with a different subtype of constraint 'BonusCommon'

Typescript playground

【问题讨论】:

    标签: reactjs typescript typescript-generics


    【解决方案1】:
    export type BonusCommon = {
      offerId: number
    }
    
    type RenderChildren<T> = {
      onClick: () => void
    } & T
    
    export type BonusProps<T extends BonusCommon> = {
      children: (props: RenderChildren<T>) => any
    } & T
    
    export const Bonus = <T extends BonusCommon>({
      children,
      ...rest
    }: BonusProps<T>) => {
      const onClick = () => {}
    
      return children({
        onClick,
        ...(rest as unknown as T),
      })
    }
    
    • offerId 需要成为 rest 的一部分,这样你就可以说它是一个类型 T
    • 现在{ onClick: () =&gt; void; } &amp; Omit&lt;BonusProps&lt;T&gt;, "children"&gt; 类型与RenderChildren&lt;T&gt; 不匹配所以,首先转换unknown,然后转换为T

    【讨论】:

      【解决方案2】:

      没有type assertion,也可以推断出你想要的所有类型。

      为了做到这一点,你只需要curry你的组件。

      import React from 'react'
      
      export type BonusCommon = {
        offerId: number
      }
      
      type ClickHandler = {
        onClick: () => void
      }
      
      export type BonusProps<T> = T & ClickHandler
      
      type RenderChildren<T> = {
        children: (props: T) => JSX.Element
      }
      
      export const withRest = <
        Keys extends PropertyKey,
        Value,
        Rest extends Record<Keys, Value>,
        >(initialProps: Rest) => <
          OfferId extends BonusCommon,
          Children extends RenderChildren<BonusCommon & ClickHandler & Rest>
        >({
          children,
          offerId,
        }: OfferId & Children) => {
          const onClick = () => { }
          return children({ ...initialProps, offerId, onClick })
        }
      const Bonus = withRest({ hello: 1 })
      
      const jsx = <Bonus offerId={42}>{
        (elem) => {
          elem.offerId // ok
          elem.hello // ok
      
          elem.onClick // ok
          return <div></div>
        }
      
      }</Bonus>
      

      Playground

      withRest 是一个函数,它需要 rest 道具并返回有效的 React 组件。返回的组件反过来知道 Rest 道具并能够处理它们。

      您可能已经注意到,children 回调参数知道所有道具。

      相关答案:Firstsecond 和我的article

      如果您想了解有关参数推理的更多信息,可以阅读this

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-07-07
        • 1970-01-01
        • 1970-01-01
        • 2020-08-20
        • 1970-01-01
        • 2021-11-18
        • 2022-06-10
        • 1970-01-01
        相关资源
        最近更新 更多