[当子代可以是React节点或函数时如何使用React.FC 类型

问题描述 投票:2回答:3

我有这个示例组件

import React, { FC, ReactNode, useMemo } from "react";
import PropTypes from "prop-types";

type Props = {
  children: ((x: number) => ReactNode) | ReactNode;
};

const Comp: FC<Props> = function Comp(props) {
  const val = useMemo(() => {
    return 1;
  }, []);

  return (
    <div>
      {typeof props.children === "function"
        ? props.children(val)
        : props.children}
    </div>
  );
};

Comp.propTypes = {
  children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired
};

export default Comp;

我的目的是组件的children属性可以是

  • a node,描述为

    可以呈现的任何东西:数字,字符串,元素或包含这些类型的数组(或片段)。

  • function,(或“渲染道具”),它仅从组件内部获取一个值并返回另一个node

这里的要点很明确,children可以是一个(node,几乎所有内容),也可以是另一个(仅是function

问题

但是类型检查我面临以下问题。

  • 如果我保留此处显示的代码,则会在? props.children(val)行上收到以下错误消息

    此表达式不可调用。并非'Function |类型的所有组成部分(((x:number)=> ReactNode)| (字符串和{})| (数字和{})| (false和{})| (true和{})| ({}和字符串)| ({}和数字)| ({}和false)| ({}和true)| ((((x:number)=> ReactNode)和字符串)

我不明白此错误。

  • 如果我将Props类型更改为
type Props = {
  children: (x: number) => ReactNode;
};

并依靠React自己的type PropsWithChildren<P> = P & { children?: ReactNode };处理children不是函数的情况,然后得到错误

((属性)子代?:PropTypes.Validator React.ReactNode>类型'Validator'不能分配给类型'Validator ReactNode>'。类型'ReactNodeLike'不可分配给类型'(x:number)=> ReactNode'。类型'string'不能分配给类型'(x:number)=> ReactNode'.ts(2322)Comp.tsx(5,3):期望的类型来自属性'children',该属性在此处声明为type

在线children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired

唯一的解决方案是将Props类型保留为

type Props = {
  children: (x: number) => ReactNode;
};

并且还将Comp.propTypes更改为children: PropTypes.func.isRequired,这是我想要的[[not,因为我想明确。

问题

我如何使代码保持明确,如在本问题开始时所介绍的,并且还没有类型检查对我抛出错误?

CodeSandbox link

javascript reactjs typescript react-typescript
3个回答
1
投票
我认为全球联盟可能会有所帮助:

type Props = { children: ((x: number) => ReactNode); } | { children: ReactNode; };


0
投票
另一种可行的解决方案,不需要在组件编写过程中以任何方式写Props声明或以其他方式改写其他任何内容,都是

strictly定义props参数的类型,例如这个

type Props = { children: ((x: number) => ReactNode) | ReactNode; }; const Comp: FC<Props> = function Comp(props: Props) { // we strictly define the props type here ... } Comp.propTypes = { children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired };
我不确定100%为什么会有所作为,我的直觉是我们将Props定义“强制”到类型检查器中,因此我们限制了可能的范围。 

0
投票
为什么会出错?

有两个原因,为什么会出现这个奇怪的错误:

    React.FC类型已经包含类型为React.FCdefault children类型。
  1. [ReactNode是非常ReactNode,您可能应该避免使用更强的类型。

  • 首先,这是内置的React类型:

    weak type


    1。)您使用type ReactText = string | number; type ReactChild = ReactElement | ReactText; interface ReactNodeArray extends Array<ReactNode> {} type ReactFragment = {} | ReactNodeArray; type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined; interface FunctionComponent<P = {}> { (props: PropsWithChildren<P>, context?: any): ReactElement | null; propTypes?: WeakValidationMap<P>; contextTypes?: ValidationMap<any>; defaultProps?: Partial<P>; displayName?: string; } type PropsWithChildren<P> = P & { children?: ReactNode }; 键入您的FC<Props>。内部Comp

    已经包含一个键入为FCchildren声明,该声明与ReactNode中的children定义合并:

    Props

    2。)如果再查看type Props = { ..., children: ReactNode & (((x: number) => ReactNode) | ReactNode) }; // this is how the actual/effective props rather look like 类型,您会发现类型变得相当复杂。更糟糕的是:ReactNode通过ReactNode包含类型{}

    这是除null和undefined之外的所有内容的超类型! (我不知道,为什么他们显然必须走这条路;也许ReactFragment会有所改善)。

    因此,您可以根据需要使this issue类型更宽。

    这也是导致该错误的原因-类型防护children无法再将类型混乱适当地缩小到typeof props.children === function"


    解决方案

    除了function,您可以完全省略类型your solution。最后只是一个特殊功能,其道具包括。您仍要自定义类型FC<Props>。这将为编译器和IDE显示提供更强大,更轻松的类型。如果我将您的children定义为Anything that can be rendered,则可能是:

    children

    您甚至可以定义自己的import React, { ReactChild, ReactPortal } from "react";
    type Children = ReactChild | Array<Children> | ReactPortal
    
    type Props = {
      children: ((x: number) => Children) | Children;
    };
    
    const Comp = (props: Props) => {...}
    版本,该版本确实具有FC中的所有内容,但宽泛的React.FC类型除外:

    children

    PS:我没有提到type FC_NoChildren<P = {}> = { [K in keyof FC]: FC[K] } & // propTypes etc.
    { (props: P, context?: any): ReactElement | null } // call signature
    
    const Comp: FC_NoChildren<Props> = props => {...}
    -IMO,您可以更好地使用TS来接受编译时安全。

    希望对您有意义,欢呼!

  • © www.soinside.com 2019 - 2024. All rights reserved.