是否有 React Function 组件的类型,包括返回片段、空值、字符串等?

Is there a typing for React Function components that includes returning fragments, nulls, strings etc?

我的问题是 clarification/update 这个高票数问题:

在我看来,当你想要 return 像 string、null 等原语的函数组件时,TypeScript 不会很好地发挥作用

例如,假设您有一个需要渲染道具的组件:

import React from "react"; 

type User = {
    id: string; 
    name: string; 
    imgSrc: string; 
}


type UserProfilePageProps = {
    RenderUserPanel: React.ComponentType<{user: User}>; 
}

export const fetchUser = async () : Promise<User> => {
    return {
        id: "123", 
        imgSrc: "placeholdit", 
        name: "Joe Bloggs"
    }
}


const UserProfilePage =  (props: UserProfilePageProps) => {
    const {RenderUserPanel} = props; 
    const [user, setUser] = React.useState<null |User>(null); 

    React.useEffect(()=> {
        fetchUser().then(user => setUser(user)); 
    }, []);

    return <section>
        <h2>User Details</h2>
        {user && <RenderUserPanel user = {user}/>}
    </section> 
}

现在我可以创建应该以四种不同方式实现此渲染道具的组件:

class ClassBasedUserPanel extends React.Component<{user: User}> {
    render() {
        return <div> 
                {this.props.user.name}
        </div>
    }
}

class ClassBasedUserPanelThatReturnsString extends React.Component<{user: User}> {
    render() {
        return  this.props.user.name; 
    }
}

const FunctionBasedUserPanel = (props: {user: User}) => {
    return <div> 
      {props.user.name}
    </div>
}

const FunctionBasedUserPanelThatReturnsString = (props: {user: User}) => {
    return  props.user.name;
}

但是,FunctionBasedUserPnelThatReturnsAString 不适合 React.ComponentType<{user: User}>;,因为 React.FunctionComponent 必须 return 一个 React.ReactElement 而字符串不是。

另一方面,

React.ClassComponent 没有此限制。

const Main = () => {
    return <div> 
        <UserProfilePage RenderUserPanel = {ClassBasedUserPanel}/>
        <UserProfilePage RenderUserPanel = {ClassBasedUserPanelThatReturnsString}/>
        <UserProfilePage RenderUserPanel = {FunctionBasedUserPanel}/>
        
        {/* ERROR! */}
        <UserProfilePage RenderUserPanel = {FunctionBasedUserPanelThatReturnsString}/>

    </div> 
}

本质上,我想要的是这样一种类型:


type TypeImLookingFor<P = {}>  = React.ComponentType<P> | (props: P) => ReactNode; 

这个存在吗?有什么理由不应该存在吗?

注意:我目前正在使用 React 16。也许这是在 React 17 或 18 中修复的问题。

我现在要 post 我自己的 React 16 最佳解决方案 - 但我欢迎其他答案,特别是如果有来自 React 17/18 的更新。

按照我的建议扩大类型的问题是它实际上不起作用。

举个例子:

type TypeImLookingFor<P = {}>  = React.ComponentType<P> | ((props: P) => React.ReactNode); 

class ComponentA extends React.Component {
    render() {
        return "aaa"; 
    }
}

const ComponentB = () => {
    return "bbb"; 
}

// They do match the type signature
const A: TypeImLookingFor = ComponentA; 
const B: TypeImLookingFor = ComponentB; 

const Main =  () => {
    return <div> 
        <ComponentA/> 
        
        {/* 
        'ComponentB' cannot be used as a JSX component.
        Its return type 'string' is not a valid JSX element.ts(2786) */}
        <ComponentB/>
    </div>;

}

也就是说,如果您想以 'JSX-y' 方式使用函数组件 - 您不能 return 字符串、数字等,即使您可以使用 class 组件.

我认为最好的解决方案是:

  1. 将函数组件的 return 类型明确声明为 React.ReactElement
  2. 如果他们return输入字符串或数字等,将其包装在片段中
const ComponentC = () : React.ReactElement => {
    //Type 'string' is not assignable to type 'ReactElement<any, string | JSXElementConstructor<any>>'.ts(2322)
    return "aaa"; 
}

const ComponentD = () : React.ReactElement => {
    return <> aaa </>
}

const Main2 =  () => {
    return <div> 
        <ComponentA/> 
        <ComponentD/>
    </div>;

}