ComponentType、ComponentProps、typeof傻傻分不清楚
前言
每次写高阶函数总是经常用到ComponentType和ComponentProps,总是用得感觉懂点又不是完全懂,你说一点不懂呢。不可能🙅、绝对不可能!!!但是每次用的时候又不知道怎么使用。今天一起来彻底搞清楚。
export const withAlert = (
withAlertconfig: WithAlertConfig,
) => {
return function <T,R = React.ComponentProps<React.ComponentType<T>>>(
ModalComponent: React.ComponentType<T>,
) {
return (config: R) => {
return <ModalComponent {...config} />;
}
}
}

如果能看懂上面函数的类型,那么你可以越过本文章。如果你不是很清楚或者T,R分别代表什么,那么你可以花一点点时间耐心阅读下,或许你会有收获。
前置知识
类组件与函数式组件
类组件指的是使用class语法定义的React组件,例如:
class App extends React.Component {
render() {
return <div>Hello</div>
}
}
这里App就是一个组件类,它有以下特点:
- 使用class语法定义
- 继承自React.Component
- 持有状态state和生命周期方法
- 渲染UI通过实现render方法
- 可以添加其他自定义方法
- 使用实例化组件
组件类和函数组件(const App = () => {})的区别在于:
- 类组件是class,函数组件是函数
- 类组件可以管理状态,函数组件通过hooks管理状态
- 类组件有生命周期方法,函数组件有Effect Hook
组件类与类组件
类组件(Class Component)
- 使用 ES6 class 语法定义的 React 组件
- 包含继承自 React.Component 的类定义
- 例如:
class MyComponent extends React.Component {
//...
}
组件类 (Component Class)
- 更广义的说法,表示所有组件的类/类型
- 比如函数组件也有对应的函数类型
- 所以函数组件的类型也可称为组件类
区别
- 类组件具体指用 class 定义的组件
- 组件类泛指所有组件的类型/类
关系
- 类组件是一种组件类,是更具体的说法
- 组件类是更广泛的概念
所以综上:
- 类组件是具体的 class 组件
- 组件类是泛指组件的类型
二者可以看作一个普通名词和一个更泛泛的概念的关系。
直入主题 - 三者区别
typeof MyComponent
- 表示组件类MyComponent本身的类型
- 是个函数类型,包含调用签名、静态属性等
- 用于描述组件类,表示传入props可以实例化出组件
React.ComponentType
- React提供的公共类型,通用表示一个组件类的类型
- 例如:
type AppType = React.ComponentType<AppProps>
- 作用和typeof基本一致,都表示组件类类型
React.ComponentProps
- 用于提取组件类的props类型,得到一个接口类型
- 例如:
type AppProps = React.ComponentProps<typeof App>
- 和前两个表示组件类类型不同,表示实例的props类型
总结:
- typeof Component 或 React.ComponentType :组件类类型
- React.ComponentProps :组件props类型

看到这里是不是还是有点懵逼,接下来来几个生动的例子
typeof 函数式组件 VS typeof 类组件
定义一个函数式组件与一个类组件
class ClassComponet extends React.Component<{title:string}> {
render() {
return <div>ClassComponet</div>;
}
}
const FunctionComponet = (props:{title:string}) => {
return <div>FunctionComponet</div>;
}
type ClassComponetType = typeof ClassComponet;
这里的ClassComponetType会被推断如下类型:
interface MyComponentType {
(props: {title:string}): ReactElement;
propTypes?: WeakValidationMap<{title:string}>;
// ...其他静态属性
}
type FunctionComponetType = typeof FunctionComponet
这里的FunctionComponetType会被推断如下类型:
(props: {title: string}) => React.ReactElement;
React.ComponentType
类组件
type ClassComponentType = React.ComponentType<typeof ClassComponent>
type ClassComponentType = {
new(props: Props, context?: any): React.Component<Props>;
propTypes?: PropTypes;
defaultProps?: Partial<Props>;
displayName?: string;
// ...其他静态属性
}
函数式组件
React.ComponentType会报错,具体原因如下:
在react-jsx.d.ts 中,React.ComponentType 定义如下:
interface ComponentType<P = {}> {
new (props: P, context?: any): Component<P, any>;
propTypes?: WeakValidationMap<P>;
contextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>;
displayName?: string;
}
export type ComponentClass<P = {}, S = ComponentState> = ClassType<
P,
S,
Component<P, S>
>;
export type ComponentType<P = {}> = ComponentClass<P>;
它包含一个构造函数签名,接收 props 和 context;函数组件是纯函数,不能被构造,所以传入会报错。需要获取函数组件的组件类直接使用typeof FunctionComponent即可
React.ComponentProps
函数组件的Props的类型
type A = typeof FunctionComponet
type B = React.ComponentProps<typeof FunctionComponet>
这里的 A 和 B 的类型是等价的,都表示 Component 组件的 props 类型 Props
类组件的Props类型
type C = React.ComponentProps<typeof ClassComponet>
type D = React.ComponentProps<React.ComponentType<typeof ClassComponet>>
- 方式1获取实例 props 类型,不能扩展
- 方式2获取类类型的静态 props 类型,可以扩展 👍
现在我们回头来看下我们最开始定义的函数,
export const withAlert = (
withAlertconfig: WithAlertConfig,
) => {
return function <T,R = React.ComponentProps<React.ComponentType<T>>>(
ModalComponent: React.ComponentType<T>,
) {
return (config: R) => {
// 巴拉巴拉 这里是自己的业务逻辑
<ModalComponent {...config} />;
}
}
}
下面的使用例子很重要,你看完就会明白withAlert函数的作用。
type ModalProps = {
title?:React.Node;
open:boolean;
}
// 定义一个Modal组件,也可以是Antd 的Modal
const Modal:React.FC<AntdModalProps & {
name:string;
// 这里是自己定义的一些参数
}> = ({name,...rest}) => {
return <AntdModal {...rest}>
// 这里是弹框里面的内容,可以自己定义
</AntdModal>
}
// 这里传入一些默认的配置,如组件打开的参数,点击确认的事件等,可以将此函数暴露为一个通用的函数
const launchModal = withAlert({
visibleKey: 'open',
okEventKey: 'onOk',
cancelEventKey: 'onCancel',
afterCloseEventKey: 'afterClose',
});
// 每次使用只需要使用launchModal返回一个打开弹框的函数
const alert_modal = launchModal(Modal)
// 打开弹框
<button onClick={()=>{
alert_modal({
title:'审批',
onFinish:async (values)=>{
return Promise.resolve('ok')
},
name:'章三'
})
}}>打开弹框</button>
-
withAlert是一个函数传入类型为WithAlertConfig的withAlertconfig参数,函数返回了一个函数。
-
返回函数接收一个组件作为参数,然后返回一个可以打开弹框的方法。
-
function <T,R = React.ComponentProps<React.ComponentType<T>>>
,这里的T就是ModalProps,这里的R指的是通过组件类获取到的Props的类型,其实就是可扩展的ModalProps -
将R作为alert_modal的config的类型,其实就是ModalProps.
-
剩下的就是你自己的逻辑,你可以通过事件打开弹框,并设置open或者visible参数,这样就不需要手动维护open或者visible
总结
哪有什么总结,总结在上面👆直入主题 - 三者区别
你以为就完了,其实没有👇分享一些小心得
-
使用高阶函数创建一个让所有组件都拥有请求能力的WithAction。例如:WithAction(Button)、WithAction(Select)
-
使用高阶组件封装一个不需要维护open/visible状态的WithAlert。例如:WithAlert(Modal)、WithAlert(Draw)

文章如有理解错误🙅的地方请大佬轻点喷🙇
转载自:https://juejin.cn/post/7276261829726437436