这是一个非常有趣的问题。前段时间,我花了很多时间研究 react 内置类型。这是我发现/理解的:
如果您使用jsx
语法,您将无法推断出某些特定类型。
看一下类型定义 FC
interface FunctionComponent<P = {}> {
(props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
propTypes?: WeakValidationMap<P>;
contextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>;
displayName?: string;
}
type VFC<P = {}> = VoidFunctionComponent<P>;
interface VoidFunctionComponent<P = {}> {
(props: P, context?: any): ReactElement<any, any> | null;
propTypes?: WeakValidationMap<P>;
contextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>;
displayName?: string;
}
您可能已经注意到,每个 FUnctionCOMponent 都返回ReactElement<any,any>
。
这是Reactlement
这样的:
interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
type: T;
props: P;
key: Key | null;
}
我们感兴趣的是T
- 类型泛型参数。它延伸JSXElementConstructor
type JSXElementConstructor<P> =
| ((props: P) => ReactElement | null)
| (new (props: P) => Component<P, any>);
JSXElementConstructor
只是一个递归。这里没有任何帮助。
React Native 语法是一种更有趣的方式。
考虑下一个例子:
const foo = React.createElement("a"); // ok
这就是魔法发生的地方。看一下createElement
打字:
// DOM Elements
// TODO: generalize this to everything in `keyof ReactHTML`, not just "input"
function createElement(
type: "input",
props?: InputHTMLAttributes<HTMLInputElement> & ClassAttributes<HTMLInputElement> | null,
...children: ReactNode[]): DetailedReactHTMLElement<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
function createElement<P extends HTMLAttributes<T>, T extends HTMLElement>(
type: keyof ReactHTML,
props?: ClassAttributes<T> & P | null,
...children: ReactNode[]): DetailedReactHTMLElement<P, T>;
function createElement<P extends SVGAttributes<T>, T extends SVGElement>(
type: keyof ReactSVG,
props?: ClassAttributes<T> & P | null,
...children: ReactNode[]): ReactSVGElement;
function createElement<P extends DOMAttributes<T>, T extends Element>(
type: string,
props?: ClassAttributes<T> & P | null,
...children: ReactNode[]): DOMElement<P, T>;
// Custom components
function createElement<P extends {}>(
type: FunctionComponent<P>,
props?: Attributes & P | null,
...children: ReactNode[]): FunctionComponentElement<P>;
function createElement<P extends {}>(
type: ClassType<P, ClassicComponent<P, ComponentState>, ClassicComponentClass<P>>,
props?: ClassAttributes<ClassicComponent<P, ComponentState>> & P | null,
...children: ReactNode[]): CElement<P, ClassicComponent<P, ComponentState>>;
function createElement<P extends {}, T extends Component<P, ComponentState>, C extends ComponentClass<P>>(
type: ClassType<P, T, C>,
props?: ClassAttributes<T> & P | null,
...children: ReactNode[]): CElement<P, T>;
function createElement<P extends {}>(
type: FunctionComponent<P> | ComponentClass<P> | string,
props?: Attributes & P | null,
...children: ReactNode[]): ReactElement<P>;
这个函数什么都知道。你要IntrinsicElements
吗?没问题!
const createElement = <T extends keyof JSX.IntrinsicElements>(elem: T) =>
React.createElement(elem);
const test = createElement("a"); // ok
const test_ = createElement(null); // error
类型,您感兴趣的 int 是: DetailedReactHTMLElement<HTMLAttributes<HTMLElement>, HTMLElement>
您可以在createElement
重载中找到它。
随意尝试。
前段时间我写了两篇关于这个话题的文章。你可以在我的博客中找到它们:
打字react儿童
输入react返回类型
不严格相关,但仍然可能对您感兴趣:键入 react props
我不确定我是否已经回答了您的问题,但希望我的回答对您有所帮助
更新
你也可以覆盖FUnctionComponent
接口,它部分帮助但仍然允许 React.Fragment:
interface FunctionComponent<P = {}> {
(props: PropsWithChildren<P>, context?: any): React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>
}
// ok
const Foo: FunctionComponent<{ label: string }> = (props) => {
return <div></div>
}
// error
const Bar: FunctionComponent<{ label: string }> = (props) => {
return 42
}
// error
const Baz: FunctionComponent<{ label: string }> = (props) => {
return null
}
// error
const Bat: FunctionComponent<{ label: string }> = (props) => {
return 'str'
}
const x = React.Fragment
// no error
const Fragment: FunctionComponent<{ label: string }> = (props) => {
return <></>
}