如何为React.Children.map定义打字稿

时间:2019-04-01 22:00:26

标签: reactjs typescript

我有一个查看提供的子代的函数,如果找到了特定的元素类型,它将自动为其添加一些属性。

该函数的调用方式如下:

render () {

    const { children, name, className } = this.props;

    return (
        <div className={className}>
            {this.enrichRadioElements(children, name)}
        </div>
    )
}

,它是这样实现的:

enrichRadioElements = (children: Array<any>, name: string) => (
    React.Children.map(children, child => {
        if (!React.isValidElement(child)) {
            return child;
        }

        //@ts-ignore
        if (child.props.children) {
            child = React.cloneElement(child, {
                //@ts-ignore
                children: this.enrichRadioElements(child.props.children, name)
            });
        }

        if (child.type === Radio) {
            return React.cloneElement(child, { 
                onChange: this.handleFieldChange,
                selectedValue: this.state.selectedValue,
                name: name
            })
        }
        else {
            return child;
        }
    })
)

这两个//@ts-ignore注释是我试图通过编写满足打字稿的代码来摆脱的。如果删除第一个,则显示的错误消息是:

  

类型为“ {}”的属性“子级”不存在。(ts-2339)

如何正确修改代码,以便删除//@ts-ignore注释?我确实查看了child.props的定义,发现了这一点:

interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
    type: T;
    props: P;
    key: Key | null;
}

看起来有任何类型的“道具”(如果我正确阅读的话),但是打字稿无法识别children属性。

1 个答案:

答案 0 :(得分:2)

问题有两点。我首先将children: Array<any>更改为children: React.ReactNode。您已经在其中进行了检查,以将类型从ReactNode缩小为ReactElement。诀窍是:1.在isValidElement中使用通用类型参数,并且2.在变量elementType上使用带有类型分配的新变量,而不是处理和修改child参数。 EnrichedChildren可能需要更新以匹配您的用例。

interface EnrichedChildren {
  onChange(): void
  selectedValue: string
  name: string
  children?: React.ReactNode
}

enrichRadioElements = (children: React.ReactNode, name: string): any =>
  React.Children.map(children, child => {
    if (!React.isValidElement<EnrichedChildren>(child)) {
      return child
    }

    let elementChild: React.ReactElement<EnrichedChildren> = child
    if (child.props.children) {
      elementChild = React.cloneElement<EnrichedChildren>(elementChild, {
        children: this.enrichRadioElements(elementChild.props.children, name),
      })
    }

    if (elementChild.type === 'Radio') {
      return React.cloneElement(elementChild, {
        onChange: () => {},
        selectedValue: 'value',
        name: name,
      })
    } else {
      return elementChild
    }
  })