SYSTEM: ONLINE
VER. ...
SLEET LOG
SLEET'S LOG
/2022年10月1日/3 MIN READ

TypeScript在React中的类型定义

TypeScript React

但是当我用了半年 TS、前几天用 JS 起了个小项目的时候,才感觉没类型提示真的有点不习惯了。JS 不会给你提示,要一通 console.log 大法慢慢调试。而 TS 则会预先对项目静态编译,这样可以避免拼写错误问题,写好的接口类型也方便多人协作时快速了解变量结构。

而困扰我在 React 里写 TS 的最大问题,其实就是类型该如何写,包括 React 中出现的特定结构的类型、事件类型等等,以及这些类型应该怎么才算写得比较规范。

约等于翻译了一下参考文档,仅作个人写项目参考

React 中常见的类型

函数式组件

React.FC<Props>|React.FunctionComponent<Props>

typescript
import { FC } from 'React'; const Component: FC<Props> => {}

class 式组件

React.Component<Props, State>

typescript
import { Component } from 'React'; class component extends Component<Props, State> => {}

高阶组件(HOC)

React.ComponentProps<typeof XXX>

typescript
import { Diff } from 'utility-types' interface InjectedProps { // ... } const withState = <WrappedProps extends InjectedProps>( WrappedComponent: React.ComponentType<WrappedProps> ) => { type HOCProps = Diff< React.ComponentProps<typeof WrappedComponent>, InjectedProps > & { // 填写这个HOC扩充的props }; } type HOCState = { readonly state: string; } handleChangeState = () => { this.setState({ state: 'new state' }) } class HOC extends React.Component<HOCProps, State> { render(){ const {...restProps} = this.props; return <WrappedComponent {...(restProps as WrappedProps)} state={state} onChangeState={handleChangeState} /> } } return HOC }

React 元素

React.ReactElement|JSX.Element

typescript
const element: React.ReactElement = <a /> || <Component />;

React 节点

React.ReactNode

除了元素,还包括字符串、数字、布尔值、null、undefined 等

typescript
const node: React.ReactNode = <a /> || <Component /> || "string";

style 类型

React.CSSProperties

typescript
const style: React.CSSProperties = { color: "red", fontSize: "12px", }; const element = <div style={style} />;

HTML 属性类型

React.XXXHTMLAttributes<HTMLXXXElement>

用于拓展 HTML 元素的属性

typescript
// 例如定制一个Button组件,该type可以保留button的原有属性 const Button:React.FC<Props & React.ButtonHTMLAttributes<HTMLButtonElement> = props=>{...} <Button type="button" disabled={true} />

声明事件处理程序

React.ReactEventHandler<HTMLXXXElement>

typescript
const handleChange: React.ReactEventHandler<HTMLInputElement> = (e) => { console.log(e.target.value); }; <input onChange={handleChange} />;

具体事件类型

React.XXXEvent<HTMLXXXElement>

一些常见的事件示例:ChangeEvent, FormEvent, FocusEvent, KeyboardEvent, MouseEvent, DragEvent, PointerEvent, WheelEvent, TouchEvent

typescript
const handleChange: React.ChangeEvent<HTMLInputElement> = (e) => { console.log(e.target.value); }; <input onChange={handleChange} />;

常见的编写需求

定义默认 Props

Props 的 type 可以使用 Partial,保证在使用组件时可以只传入部分 Props。

建议返回类型选择 JSX.Element 而非 React.FC,因为 React.FC 在 props 有默认值时可能会不正常工作

typescript
type Props{ name: string; age: number; } export const Component: React.FC<Props> = (props):JSX.Element => { const { name, age } = props; return <div>{name} {age}</div> } Component.defaultProps = { name: 'name', age: 0, }

另外,在Typescript 配合 React 实践 发现一个有意思的利用高阶组件设置默认 props 的写法,供参考

TypeScript
export const withDefaultProps = < P extends object, DP extends Partial<P> = Partial<P> >( defaultProps: DP, Cmp: ComponentType<P>, ) => { // 提取出必须的属性 type RequiredProps = Omit<P, keyof DP>; // 重新创建我们的属性定义,通过一个相交类型,将所有的原始属性标记成可选的,必选的属性标记成可选的 type Props = Partial<DP> & Required<RequiredProps>; Cmp.defaultProps = defaultProps; // 返回重新的定义的属性类型组件,通过将原始组件的类型检查关闭,然后再设置正确的属性类型 return (Cmp as ComponentType<any>) as ComponentType<Props>; };

组件中将传入子组件

定义 props 的 type 时可以使用React.PropsWithChildren<{type}>,如此组件默认会接收传入的子组件,而不用另行定义

typescript
type Type = { name: string; age: number; }; type Props = React.PropsWithChildren<Type>; export const Component: React.FC<Props> = (props) => { const { children, ...rest } = props; return <div {...rest}>{children}</div>; };

使用 Context

  1. Context

定义并创建默认 props + 使用React.createContext创建 Context

typescript
// 定义默认props类型 type Themes = { dark: React.CSSProperties; light: React.CSSProperties; }; // 创建默认props export const themes: Themes = { dark: { color: "black", }, light: { color: "white", }, }; // 定义Context的接收类型,也即传递的全局变量类型 export type ThemeContextProps = { theme: CSSProperties; toggleTheme?: () => void; }; // 创建context const ThemeContext = React.createContext<ThemeContextProps>({ theme: themes.dark, }); // 导出context export default ThemeContext;
  1. Provider

定义全局变量

TypeScript
import ThemeContext, { themes } from './theme-context'; export const ThemeProvider: ReactElement = () => { const [state,setState] = useState<CSSProperties>({theme: themes.light}) const toggleTheme = () => setState( (state) => ({ theme: state.theme === themes.light ? themes.dark : themes.light, })) return ( <ThemeContext.Provider value={{theme,toggleTheme}}> <Component/> </ThemeContext.Provider> ) }
  1. Consumer

子组件消费

TypeScript
export const Component:ReactElement = (props:Props) => { return ( <ThemeContext.Consumer> { ({theme,toggleTheme})=><div style={theme} onClick={toggleTheme}/> } </ThemeContext.Consumer> ) }

参考:

~~(完全参考)~~react-redux-typescript-guide

TypeScript 配合 React 实践

Article Index