在我学习typescript
时,想在react
中使用typescript
写代码,从头开始的时候是懵逼的,因为官方文档并没有使用typescript
的教程,多是自己在网上查,自己看定义摸索
所以今天把我用过的,总结归纳一下,希望能帮助到同样在摸索的同学
以下代码react
版本为16.13.1
,在create-react-app
官方typescript
模版中无报错
一些React
的内置类型
React.ReactElement
—— 使用React.createElement
创建的,可以简单理解为React
中的JSX
的元素React.ReactNode
—— <div>xxx</div>
xxx的合法类型React.CSSProperties
—— 组件内联的style
对象的类型React.RefObject
—— React.createRef
创建的类型,只读不可改React.MutableRefObject
—— useRef
创建的类型,可以修改组件声明分为类组件和函数组件
类组件使用的定义主要为React.Component<P,S>
和React.PureComponent<P,S,SS>
interface AppProps { value: string; } interface AppState { count: number; } class App extends React.Component<AppProps, AppState> { static defaultProps = { value: "", }; state = { count: 0, }; } 复制代码
React.Component<P,S>
这里的P
是props
的类型,S
是state
的类型,可以写为React.Component<AppProp>
,因为state
的类型会自己推断
在上面PureComponent
中还有个SS
,这个SS
是getSnapshotBeforeUpdate
的返回值
函数组件定义的方式简单来看有两种,一种是使用React.FC
,一种是直接给props
写上定义
interface AppProps { value?: string; } const App: React.FC<AppProps> = ({ value = '', children }) => { // ... }; 复制代码
React.FC
的意思是FunctionComponent
,如果使用了React.FC
,它在定义里就已经规定好了children
的类型和函数的返回值,所以可以直接用children
如果是直接给props
写上定义,就需要自己定义children
的类型
interface AppProps { value?: string; children?: React.ReactNode; // 自己定义children的类型 } function App({ value = "", children }: AppProps) { return <>{children}</>; } 复制代码
使用function
来定义而不是箭头函数的优点是可以使用泛型组件
hooks
的声明如果不知道如何用ts
定义可以直接点进去看看
useState
可以使用泛型传参或者自动推断
const [state, setState] = useState(''); // state的类型为string,自动推断 const [state, setState] = useState<string>(); // state的类型为 string | undefined // 给初值 const [state, setState] = useState<string | null>(null); // state的类型为 string | null 复制代码
useRef
同样也会自动推断
const ref = useRef(""); // ref.current的类型为 string // 泛型 type Value = { value: string }; const ref = useRef<Value>({ value: "" }); // ref为html元素 const ref = useRef<HTMLDivElement>(null); return <div ref={ref} />; 复制代码
需要注意的是如果ref
为元素,那么初始值得写个null
才不会报错
useReducer
相对来说要写的更多一点,可以自动推断,所以不需要手动写泛型类型(其实我也不知道手动写怎么写Orz)
// state类型 interface ReducerState { value: string; } // action类型 interface AnyAction { type: string; [key: string]: any; } // reducer函数 const reducer: React.Reducer<ReducerState, AnyAction> = (state, action) => { switch (action.type) { default: return state; } }; // 初始值 const initialState: ReducerState = { value: "" }; const [state, dispatch] = useReducer(reducer, initialState); // state 的类型为 ReducerState // dispatch 的类型为 React.Dispatch<AnyAction> 复制代码
Action
也可以是多个不同的Action
的联合类型
useImperativeHandle
这个钩子可以把内部方法通过ref
暴露出去,用ts
也是要写多一点,类型都需要标注清楚
所以需要使用到React.forwardRef
,可以先看下一节
// props interface AppProps { value: string; } // useImperativeHandle获取到ref的类型 interface Handle { get: () => string; } const App = React.forwardRef<Handle, AppProps>(({ value }, ref) => { // 定义 useImperativeHandle(ref, () => ({ get: () => `handle get value : ${value}`, })); return null; }); // 使用 const handleRef = useRef<Handle>(null); // handleRef.current?.get(); return <App value="hello" ref={handleRef} />; 复制代码
有如下钩子
const useCustomHook = () => { const [state, setState] = useState(""); const set = (value: string) => { if (!value) return; setState(value); }; return [state, set]; }; // 使用 const [state, setState] = useCustomHook(); setState('hello') // This expression is not callabl 复制代码
自定钩子还需要定义返回值才行
- const useCustomHook = () => { + const useCustomHook = (): [string, (value: string) => void] => { 复制代码
React.forwardRef<T, P = {}>
只需要传props
的类型和ref
的类型,第一个T
是ref
的类型,P
是props
的类型
const App = React.forwardRef<HTMLDivElement, AppProps>(({ value }, ref) => { return <div ref={ref} />; }); // 使用 const ref = useRef<HTMLDivElement>(null); return <App value="hello" ref={ref} />; 复制代码
定义为该类型的函数可以放进React.forwardRef
函数中作为参数
// 定义 const forwardRender: React.ForwardRefRenderFunction< HTMLDivElement, AppProps > = ({ value }, ref) => { return <div ref={ref} />; }; const App = React.forwardRef(forwardRender); // 使用 const ref = useRef<HTMLDivElement>(null); return <App value="hello" ref={ref} />; 复制代码
泛型有自动推断的功能,所以useContext
就不需要再写上类型了
interface ContextType { getPrefixCls: (value: string) => string; } const context = React.createContext<ContextType>({ getPrefixCls: (value) => `prefix-${value}`, }); const App = () => { const { getPrefixCls } = useContext(context); getPrefixCls("App"); // prefix-App return null; }; 复制代码
如果使用的React.FC
定义的组件,它的children
类型默认是React.ReactNode
,需要显式转为React.ReactElement
const App: React.FC = ({ children }) => { return React.cloneElement(children as React.ReactElement, { value: "hello" }); }; // 也可以覆写定义 const App: React.FC<{ children: React.ReactElement }> = ({ children }) => { return React.cloneElement(children, { value: "hello" }); }; 复制代码
通过React.ComponentType<P>
定义的组件可以将变量名传入组件,在组件内调用,高阶组件通常会使用
interface AppProps { value: string; } const App: React.FC<AppProps> = (props) => { return null; }; // React.ComponentType定义组件 function HOC<T>(Component: React.ComponentType<T>) { return function (props: T) { return <Component {...props} />; }; } const WrappedComponent = HOC(App); // 调用 <WrappedComponent value="hello" /> 复制代码
泛型参数的组件是typescript
2.9版本新增的,第一次看见是在ant-deisgn
里
一个很简单的例子就是Select
组件
<Select<number>> <Select.Option value={1}>1</Select.Option> <Select.Option value={2}>2</Select.Option> </Select> 复制代码
// 定义泛型参数的组件 class GenericComponent<P> extends React.Component<P> { internalProp: P; constructor(props: P) { super(props); this.internalProp = props; } render() { return null; } } type Props = { a: number; b: string }; <GenericComponent<Props> a={10} b="hi" />; // OK <GenericComponent<Props> a={10} b={20} />; // Error 复制代码
function GenericComponent<P>(props: P) { const internalProp = useRef(props) return null; } 复制代码
函数组件写起来可简洁太多了…
也是多种多样
const App = () => { // React.MouseEventHandler const onClick: React.MouseEventHandler<HTMLInputElement> = (e) => { console.log(e.currentTarget.value); }; // React.ChangeEventHandler const onChange: React.ChangeEventHandler<HTMLInputElement> = (e) => { console.log(e.currentTarget.value); }; // React.FocusEventHandler const onFocus: React.FocusEventHandler<HTMLInputElement> = (e) => { console.log(e.currentTarget.value); }; return <input onClick={onClick} onChange={onChange} onFocus={onFocus} />; }; 复制代码
如果有事件不清楚该如何定义类型,可以点组件上的事件名去看看定义
需要注意的是只有e.currentTarget
才会有对应的元素类型,e.target
是没有的,它的类型是EventTarget
想不出还有啥了,想起来再补充
ts
自带了一些工具泛型,比如Omit
、Pick,
在开发的时候还是有帮助,可以看看我以前的总结
最后,祝大家身体健康,工作顺利!
太惨了,tsx连代码高亮都没有,只能说还是prismjs
好使,highlightjs
不太行