使用react + ts + antd时你必须知道的数据类型

5,214 阅读5分钟

简介

使用react + typescript + antd开发过程中,我们或多或少会困惑于如何使用正确的类型定义,本文就对在此过程中涉及的常用类型定义逐一展开说明,并附以使用案例,初衷是做个笔记方便自己开发时查看,文章编写期间有参考过其他博文,在此就不逐一例举。

一、分解react中的ts类型定义

1、react内部组件定义

  • React.ReactText
    • string
    • number
  • React.ReactChild
    • ReactText
    • React组件
  • React.ReactNode
    • ReactChild
    • ReactFragment
    • ReactPortal
    • boolean
    • null
    • undefined
const elementOrPrimitive: React.ReactNode = 'string' || 0 || false || null || undefined || <div /> || <MyComponent />;
const Component = ({ children: React.ReactNode }) => ...
  • React.CSSProperties React CSS属性,代表着Style Object在 JSX 文件中(通常用于 css-in-js)
const styles: React.CSSProperties = { flexDirection: 'row', ...
const element = <div style={styles} ...
  • React.FunctionComponent<P={}> 简写FC<P={}>,无状态组件(SFC),函数组件的类型定义,一个泛型接口,可以接受一个参数,可以不传,用来定义props的类型。
interface EditorsProps {
	detail: string
}
// const Editors:React.FunctionComponent<EditorsProps> = (props) => {
const Editors: React.FC<EditorsProps> = (props) => {
	const { detail } = props;
	return (<>{detail}</>);
};
  • Component<P,S={}>/PureComponent<P,S={}> 泛型类,接收两个参数,第一个是props的类型定义,第二个是state的类型定义(可以省略,但当有父组件传递属性方法或者定义state的时候还是需要,当没有的情况下省去,和不用TypeScript使用一样),示例代码:
import React, { Component } from 'react'
import { RouteComponentProps } from 'react-router-dom';
interface CountProps extends RouteComponentProps {//可以继承其它的接口类型
	count: number;
	asyncAddCount: (count: number) => void;
	asyncReduceCount: (count: number) => void;
}
interface CountStateType{//当需要的时候才定义
}
class Counter extends Component<CountProps, CountStateType> {
	render(): JSX.Element{
		const { count, asyncAddCount, asyncReduceCount } = this.props;
		return (
			<div>
				<h2>{count}</h2>
				<button onClick={asyncAddCount.bind(null, 10)}>Counter++</button>
				<button onClick={asyncReduceCount.bind(null, 10)}>Counter--</button>
			</div>
		)
	}
}
  • JSX.ElementReact.ReactElement<P> return返回的jsx语法类型,例如上述的render中return的就是这个类型。
const elementOnly: React.ReactElement = <div /> || <MyComponent />;
  • ComponentClass<P,S={}> 类的类型,泛型接口,可以在高阶组件中使用,当接收一个类或者函数的时候。
import React, { Context, FC, ComponentClass, createContext, useReducer } from 'react';

const ProviderContext: Context<any> = createContext('provider');

export default const RootProvider = (reducer: Function, initialState: any) => (Com: FC<any> | ComponentClass<any,any>) => {
  return () => {
    const [state, dispatch] = useReducer<any>(reducer, initialState);
    return (
      <ProviderContext.Provider value={{ state, dispatch }}>
        <Com />
      </ProviderContext.Provider >
    );
  }
}
  • Context context的类型就是他的本身,一个泛型接口。
//源码的类型定义如下:可以发现我们需要传递一个类型,从而使得里面的参数类型也是一致
interface Context<T> {
	Provider: Provider<T>;
	Consumer: Consumer<T>;
	displayName?: string;
}
  • Dispatch<any> 泛型接口,用于定义dispatch的类型,常常用于useReducer生成的dispatch中。
// 创建一个异步action的函数,返回一个包含异步action对象
const asyncAction = (dispatch: Dispatch<any>) => {
  return {
    asyncAddaction() {
      console.log('执行addActions之前: ' + Date.now());
      setTimeout(() => {
        console.log('执行addActions : ' + Date.now());
        dispatch(addActions());
      }, 1000);
    }
  }
}
  • LazyExoticComponent lazy懒加载的类型,泛型接口,可以接受各种类型的参数,视情况而定,例如:
export interface RouteType {
  pathname: string;
  component: LazyExoticComponent<any>;
  exact: boolean;
  title?: string;
  icon?: string;
  children?: RouteType[];
}
export const AppRoutes: RouteType[] = [
  {
    pathname: '/login',
    component: lazy(() => import('../views/Login/Login')),
    exact: true
  },
  {
    pathname: '/404',
    component: lazy(() => import('../views/404/404')),
    exact: true,
  },
  {
    pathname: '/',
    exact: false,
    component: lazy(() => import('../views/Admin/Admin'))
  }
]
  • RefForwardingComponent<T, P = {}> forwardRef,ref转发的类型定义,泛型接口,接收两个参数。
forwardRef(Editors) as RefForwardingComponent<any, any>;
//分别是ref的类型和props的类型,为了简单可以都定义为any
//源码类型定义如下
 interface RefForwardingComponent<T, P = {}> {
	(props: PropsWithChildren<P>, ref: Ref<T>): ReactElement | null;
	propTypes?: WeakValidationMap<P>;/
	contextTypes?: ValidationMap<any>;
	defaultProps?: Partial<P>;
	displayName?: string;
 }
  • MutableRefObject<any> 泛型接口,接收一个参数,作为useRef的类型定义,参数可以为任意类型。
const prctureRef: React.MutableRefObject<any> = useRef();
  • useState<any> hooks的useState是一个泛型函数,可以传递一个类型来定义这个hooks。
const [isShowAdd, setIsShowAdd] = useState<boolean>(false);

当然useRef也是一个泛型函数,如果想要严谨的话也可以传递给一个类型来定义,还有useReducer等都差不多。

其他:

interface IProps{
    name: React.ReactText;
    
    children?: React.ReactChild;

    header?: React.ReactNode;
}

2、react-router常用类型

  • RouteComponentProps 最常见的路由api的类型定义,里面包含了history,location,match,staticContext这四个路由api的类型定义
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
export default function Admin({ history, location, match }: RouteComponentProps) {
	return(<>这是主页</>);
}

3、react内部事件定义

  • FormEvent 一个react的form表单event的类型。
<form onSubmit={(e:FormEvent)=>{
		e.preventDefault();
}}></form>
  • ChangeEvent react的onChange事件触发的event类型,这是一个泛型接口,使用如下:
<input 
	type="text" 
	value={count} 
	onChange={(e: ChangeEvent<HTMLInputElement>) => {
		setCount(e.currentTarget.value);//HTMLInputElement表示这个一个html的input节点
}} />

可选泛型类型:HTMLSelectElementHTMLInputElementHTMLDivElementHTMLTextAreaElement等html标签的所有类型节点。 还有其他各种事件处理类型,可以在@types/react中查看。

  • SyntheticEvent<T = Element, E = Event> 泛型接口,事件包装器,即原生事件的集合,就是原生事件的组合体。

您的事件处理程序将传递 SyntheticEvent 的实例,这是一个跨浏览器原生事件包装器。(官方介绍)

<button onClick={(e:SyntheticEvent<Element, Event>)=>{}}></button>
<input onChange={(e:SyntheticEvent<Element, Event>)=>{}}/>
<form
	onSubmit={(e: SyntheticEvent<Element, Event>) => {}}
	onBlur={(e: SyntheticEvent<Element, Event>) => {}}
	onKeyUp={(e: SyntheticEvent<Element, Event>) => {}}
>
</form>

从上面可以发现,合成事件的泛型接口在任意事件上都能适用。

  • 不完全例举其他事件类型 image.png

示例代码:

import React, { FC, ReactElement, MouseEvent  } from 'react'

type Props = {
    label: string,
    children: ReactElement,
    onClick?: (e: MouseEvent<HTMLButtonElement>) => void
}

const FunctionComponent: FC<Props> = ({ label, children, onClick }: Props) => {
    return (
        <div>
            <span>
                {label}:
            </span>
            <button type="button" onClick={onClick}>
                {children}
            </button>
        </div>
    )
}

export default FunctionComponent
  • React.ReactEventHandler<HTMLElement> 通用的 React Event Handler
const handleChange: React.ReactEventHandler<HTMLInputElement> = (ev) => { ... } 

<input onChange={handleChange} ... />

二、Antd的类型定义

  • FormComponentProps 用于Form组件的使用
import React from 'react';
import { Form } from 'antd';
import { FormComponentProps } from 'antd/lib/form';
interface AddFormProps extends FormComponentProps {
}
function AddForm({ form }: AddFormProps) {
	return (
		<Form></Form>
	);
}
export default Form.create()(AddForm) as any;

里面的form的类型是WrappedFormUtils泛型接口,正常在对form赋值的时候的定义。

  • ColumnProps<any> 表格定义的columns属性的每一项的类型,参数any表示表格数据的类型,示例如下:
interface ProductType {
  key:string;
  name:string;
  desc:string
}
const columns: ColumnProps<ProductType>[] = [
  {
    title: '商品名称',
    dataIndex: 'name'
  },
  {
    title: '商品详情',
    dataIndex: 'desc'
  }
]

三、其他

  • Promise<any>
interface IResponse<T> {
  message: string,
  result: T,
  success: boolean,
}
async function getResponse (): Promise<IResponse<number[]>> {
  return {
    message: '获取成功',
    result: [1, 2, 3],
    success: true,
  }
}
getResponse()
  .then(response => {
    console.log(response.result)
  })
  • axios
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
const server: AxiosInstance = axios.create();
server.interceptors.request.use((config: AxiosRequestConfig) => {//请求拦截
    return config;
});
server.interceptors.response.use((res: AxiosResponse) => {
    if (res.status === 200) {
        res = res.data;
    }
    return res;
},(err:AxiosError)=>{});