我有这个示例组件
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,因为我想明确。
type Props = {
children: ((x: number) => ReactNode);
} | {
children: ReactNode;
};
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
定义“强制”到类型检查器中,因此我们限制了可能的范围。
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
已经包含一个键入为
FC
的children
声明,该声明与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之外的所有内容的超类型! (我不知道,为什么他们显然必须走这条路;也许
因此,您可以根据需要使this issue类型更宽。ReactFragment
会有所改善)。这也是导致该错误的原因-类型防护
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来接受编译时安全。希望对您有意义,欢呼!