![](/img/trans.png)
[英]forwarding ref to react.FC gives type error: Property ref does not exist on type 'IntrinsicAttributes & Props & { children :? ReactNode}
[英]How to use React.FC<props> type when the children can either be a React node or a function
我有這個示例組件
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
屬性可以是
一個node
,它被描述為
任何可以渲染的東西:數字、字符串、元素或包含這些類型的數組(或片段)。
一個function
(或“渲染道具”),它只是從組件內部獲取一個值並返回另一個node
這里的重點是明確的, children
可以是一個( node
,它幾乎是一切)或另一個(它只是一個function
)
但是,我在類型檢查方面面臨以下問題。
? props.children(val)
? props.children(val)
此表達式不可調用。 並非所有類型為“功能 | ”的成分 ((x: number) => ReactNode) | (字符串 & {}) | (數字和{})| (false & {}) | (真 & {}) | ({} & 字符串) | ({} & 數字) | ({} & 假) | ({} & 真) | (((x: number) => ReactNode) & 字符串)
我不明白這個錯誤。
Props
類型更改為type Props = {
children: (x: number) => ReactNode;
};
並依賴於 React 自己的type PropsWithChildren<P> = P & { children?: ReactNode };
處理children
不是函數的情況,然后我得到錯誤
(property) children?: PropTypes.Validator<(x: number) => React.ReactNode> Type 'Validator' 不能分配給 type 'Validator<(x: number) => ReactNode>'。 類型 'ReactNodeLike' 不可分配給類型 '(x: number) => ReactNode'。 類型 'string' 不可分配給類型 '(x: number) => ReactNode'.ts(2322) Comp.tsx(5, 3): 預期類型來自屬性 'children',它在類型上聲明
在線children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired
唯一的解決方案是將Props
類型保留為
type Props = {
children: (x: number) => ReactNode;
};
並將Comp.propTypes
更改為children: PropTypes.func.isRequired
,這不是我想要的,因為我想明確表示。
我怎樣才能使代碼保持明確,如本問題開頭所示,並且類型檢查不會對我拋出錯誤?
ReactNode
默認children
ReactNode
,它們與包含在Props
您自己的children
類型合並( &
)。ReactNode
是一種相當寬的類型,限制了編譯器將children
聯合類型縮小到可調用函數的能力,並結合第 1 點。 一個解決方案是省略FC
並使用比ReactNode
更窄的類型來提高類型安全性:
type Renderable = number | string | ReactElement | Renderable[]
type Props = {
children: ((x: number) => Renderable) | Renderable;
};
首先,這里是內置的 React 類型:
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 };
1.) 您使用FC<Props>
鍵入Comp
。 FC
內部已經包含了一個類型為ReactNode
的children
聲明,它與來自Props
children
定義合並:
type Props = { children: ((x: number) => ReactNode) | ReactNode } &
{ children?: ReactNode }
// this is how the actual/effective props rather look like
2.) 查看ReactNode
類型,您會發現類型變得相當復雜。 ReactNode
包括類型{}
經由ReactFragment
,這是不同之處一切的超型null
和undefined
。 我不知道這種類型形狀背后的確切決定, microsoft/TypeScript#21699暗示了歷史和向后兼容的原因。
因此, children
類型比預期更廣泛。 這會導致您的原始錯誤: type guard typeof props.children === "function"
無法正確縮小類型 " typeof props.children === "function"
" 以使其不再function
。
React.FC
最后, React.FC
只是一個函數類型,具有額外的屬性,如propTypes
、 displayName
等,帶有自以為是的、廣泛的children
類型。 此處省略FC
將為編譯器和 IDE 顯示提供更安全、更易於理解的類型。 如果我接受你的定義Anything that can be rendered
為children
Anything that can be rendered
,那可能是:
import React, { ReactChild } from "react";
// You could keep `ReactNode`, though we can do better with more narrow types
type Renderable = ReactChild | Renderable[]
type Props = {
children: ((x: number) => Renderable) | Renderable;
};
const Comp = (props: Props) => {...} // leave out `FC` type
children
自定義FC
類型你可以定義你自己的FC
版本,它包含React.FC
中的所有內容,除了那些寬children
類型:
type FC_NoChildren<P = {}> = { [K in keyof FC<P>]: FC<P>[K] } & // propTypes etc.
{ (props: P, context?: any): ReactElement | null } // changed call signature
const Comp: FC_NoChildren<Props> = props => ...
我認為全球聯盟可能會有所幫助:
type Props = {
children: ((x: number) => ReactNode);
} | {
children: ReactNode;
};
另一個有效的解決方案,並且不需要以任何不同的方式編寫Props
聲明或以不同的方式重寫任何其他內容,是在組件定義期間嚴格定義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
定義“強制”到類型檢查器,因此我們限制了可能的范圍。
自從我問了最初的問題后,我最終決定采用以下解決方案來解決我的問題:我定義了自己的函數組件類型:
//global.d.ts
declare module 'react' {
// Do not arbitrarily pass children down to props.
// Do not type check actual propTypes because they cannot always map 1:1 with TS types,
// forcing you to go with PropTypes.any very often, in order for the TS compiler
// to shut up
type CFC<P = {}> = CustomFunctionComponent<P>;
interface CustomFunctionComponent<P = {}> {
(props: P, context?: any): ReactElement | null;
propTypes?: { [key: string]: any };
contextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>;
displayName?: string;
}
}
這個解決方案
children
道具進入我的定義Component.propTypes
與 TS type Props = {...}
。 很多時候他們不會准確地映射到 1:1,我被迫使用PropTypes.any
這不是我想要的。 我將Component.propTypes
與 TS 類型一起保留的原因是,雖然 TS 在開發過程中非常好,但PropTypes
實際上會在運行時出現錯誤類型值的情況下發出警告,這是有用的行為,例如, API 響應中的字段應該是一個數字,現在是一個字符串。 這樣的事情可能會發生,這不是 TS 可以提供幫助的。
https://github.com/DefinitelyTyped/DefinitelyTyped/issues/34237 https://github.com/DefinitelyTyped/DefinitelyTyped/issues/34237#issuecomment-486374424
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.