如何在功能组件中使用 React Typescript 输入任何参数

ama*_*ter 1 javascript types typescript reactjs react-props

我有一个现有的 javascript 组件:

const RenderState = ({ state, else: elseChild = undefined, ...states }) => {
  if (state && states[state]) return states[state]
  if (elseChild) return elseChild
  return null
}

export default RenderState
Run Code Online (Sandbox Code Playgroud)

我正在尝试将其转换为 typscript 并且到目前为止:

import React from 'react'

interface Props {
  state: string
  else?: JSX.Element
  any: JSX.Element
}

const RenderState: React.FC<Props> = ({ state, else: elseChild = undefined, ...states }) => {
  if (state && states[state]) return states[state]
  if (elseChild) return elseChild
  return null
}

export default RenderState
Run Code Online (Sandbox Code Playgroud)

我遇到的问题当然any: JSX.Element是寻找一个字面上称为“any”的道具,而我想允许任何道具作为 JSX 元素,例如custom在以下示例中

这是此组件的示例用例:

import React from 'react'
import RenderState from './RenderState'

const MyComp = () => {
  const [state, setState] = React.useState<string | null>(null)
  <>
    <Button onClick={()=>{setState(null)}}>Default</Button>
    <Button onClick={()=>{setState('custom')}}>Custom</Button>
    <RenderState
      state={state}
      custom={(<p>Some custom content here...</p>)}
      else={(<p>Some default content here...</p>)}
    />
  </>
}
Run Code Online (Sandbox Code Playgroud)

ama*_*rov 7

Sonds 喜欢您的类型需要索引签名。一种天真的方法是

interface Props {
  state: string
  else?: JSX.Element
  [key: string]: JSX.Element
}
Run Code Online (Sandbox Code Playgroud)

但是,这不会编译 - 因为接口上的索引签名要求所有命名属性(stateelse)与其兼容。

相反,您可以以类型交集的形式重写它:

type Props = {
  state: string
  else?: JSX.Element
} & Record<string, JSX.Element>
Run Code Online (Sandbox Code Playgroud)

这仍然不会编译相同,但给了我们一个线索。如果string我们有一组众所周知的可能值而不是键呢?下一次迭代将是

type Key = "custom" | "another";

type Props = {
  state: Key | null
  else?: JSX.Element
} & Record<Key, JSX.Element>
Run Code Online (Sandbox Code Playgroud)

这看起来更好。但是我们如何使它可重用,以便相同的组件可以与不同的可能键列表一起使用?让我们让它通用!

所以最后的代码:

type Props<Key extends string> = {
  state: Key | null
  else?: JSX.Element
} & Record<Key, JSX.Element>
Run Code Online (Sandbox Code Playgroud)

您还需要使组件成为通用函数:

function RenderState<Key>(props: Props<Key>) { ... }
Run Code Online (Sandbox Code Playgroud)

并在您的应用中使用它:

    const [state, setState] = React.useState<"custom" | null>(null);
    <RenderState
      state={state}
      custom={(<p>Some custom content here...</p>)}
      else={(<p>Some default content here...</p>)}
    />

Run Code Online (Sandbox Code Playgroud)

编辑 1

这种方法允许在props[props.state]内部写入RenderState(结果推断为JSX.Element)。但是,它无法通过 rest 解构进行编译,如原始代码片段所示:

const {state, else: elseEl, ...rest} = props;
rest[props] // TS error here
Run Code Online (Sandbox Code Playgroud)

它揭示了我们的类型定义中的一个缺陷。我们需要确保statevalue 不包含"state""else"文字,以避免类型冲突。

更新的类型将是


type Props<Key extends string> = {
  state: Exclude<Key, "state" | "else"> | null
  else?: JSX.Element
} & Record<Key, JSX.Element>
Run Code Online (Sandbox Code Playgroud)

现在这应该可以很好地编译

const {state, else: elseEl, ...rest} = props;
rest[props]
Run Code Online (Sandbox Code Playgroud)