代码之家  ›  专栏  ›  技术社区  ›  amaster

如何在Functional Component中使用React Typescript键入任何参数

  •  0
  • amaster  · 技术社区  · 4 年前

    我有一个现有的javascript组件:

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

    我正在尝试将其转换为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
    

    我遇到的问题当然是 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>)}
        />
      </>
    }
    
    0 回复  |  直到 4 年前
        1
  •  7
  •   amakhrov    4 年前

    像你这样的字体需要索引签名。 一种天真的做法是

    interface Props {
      state: string
      else?: JSX.Element
      [key: string]: JSX.Element
    }
    

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

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

    type Props = {
      state: string
      else?: JSX.Element
    } & Record<string, JSX.Element>
    

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

    type Key = "custom" | "another";
    
    type Props = {
      state: Key | null
      else?: JSX.Element
    } & Record<Key, JSX.Element>
    

    这看起来好多了。但是,我们如何使其可重用,以便同一组件可以与不同的可能密钥列表一起使用呢?让我们把它变成通用的!

    最后的代码:

    type Props<Key extends string> = {
      state: Key | null
      else?: JSX.Element
    } & Record<Key, JSX.Element>
    

    您还需要将组件设置为通用函数:

    function RenderState<Key>(props: Props<Key>) { ... }
    

    并在您的应用程序中使用它:

        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>)}
        />
    
    

    编辑1

    这种方法允许写 props[props.state] 里面 RenderState (结果推断为 JSX.Element ). 但是,它无法使用rest-destructuring进行编译,如原始代码片段所示:

    const {state, else: elseEl, ...rest} = props;
    rest[props] // TS error here
    

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

    更新的类型将是

    
    type Props<Key extends string> = {
      state: Exclude<Key, "state" | "else"> | null
      else?: JSX.Element
    } & Record<Key, JSX.Element>
    

    现在这个应该编译得很好

    const {state, else: elseEl, ...rest} = props;
    rest[props]