|
在了解react hooks的类型之前,有必要先了解一下@types、.d.ts文件的概念及作用。
node_modules中的@types是什么?
当我们使用第三方npm包的时候,如果这个包不是编写,则没有导出类型,这时候如果在ts中导入会报错。比如这时会报错- 无法找到模块“jquery”的声明文件尝试使用 [code]npm i --save-dev @types/jquery
复制代码 (如果存在),或者添加一个包含的新声明(.d.ts[/code] 这里提示找不到jquery的类型定义 可以安装或者在中自定义类型,大多数情况我们应该使用第一种办法,如果这个库没有库再使用第二种。
types查找规则
当我们使用时ts将会默认从中获取类型声明,具体查找规则是ts编译器先在当前编译上下文找的定义,找不到则再去中查找。 在本地模块查找的类型声明作用域是在模块,在中的类型声明是全局的。在中也可以使用设置默认路径 。
模块types
- 当然在`tsconfig.json`中也可以使用`types`单独控制`@types`。`types`指定的包会被单独引入。这样全局引入就失效了。
复制代码 *.d.ts是什么
- @types下存放的文件都是.d.ts开头的文件 对应的npm包js的类型声明。 在.d.ts文件中声明的类型或者模块,在其他文件中不需要使用import导入,可以直接使用,d.ts的类型声明可以自行编写也可以使用工具声明。有2个工具
复制代码 可以使用微软的dts-gen,生成单个文件的声明dtsmake。值得注意的是如果你使用语法 在ts3.7以后是可以通过命令为js生成.ds文件。具体用法可查看TypeScript: Documentation - Creating .d.ts Files from .js files (typescriptlang.org)
介绍完前菜 现在开始进入本文正题。 一起来看下react hooks相关的类型声明吧。在文件中。
useContext
- `useContext和createContext`是结合一起使用的
复制代码 useContext定义:- function useContext<T>(context:Context<T>):T
复制代码 createContext定义:- function createContext<T>(defaultValue:T,):Context<T>
复制代码 的返回Context类型的值提供给useContext的参数。这里泛型在2个方法中是一致的,如果不指定 ts会类型推导出正确的类型。而类型 则是一个interface- interface Context<T> {
- Provider: Provider<T>;
- Consumer: Consumer<T>;
- displayName?: string | undefined;
- }
复制代码- `Provider` 拥有`value`和`children` `Consumer`拥有 `children` 类型都是`ReactNode|undefined`。想想我们这react中使用`Context`传值 是不是感觉很熟悉?看懂类型定义 再也不怕忘记api了。
复制代码 useState
定义:- function useState<S>(initialState:S| (() =>S)): [S, Dispatch<SetStateAction<S>>]
复制代码 泛型表示是用来约束类型,也可以传入返回值是的方法。返回值为2个元素的元组类型,返回和更新的方法。默认情况下会根据传入类型自动推导出类型。定义了传入的参数类型。是类型或者返回类型值的函数的联合类型。的定义为:- type SetStateAction<S> = S|((prevState:S) =>S)
复制代码 ,为上一次的,联合类型暂可以理解成或的关系。而表示setState的类型,是一个没有返回值的方法。定义也很简单- Dispatch :type Dispatch<A> = (value:A) =>void
复制代码 。 还有参数个数为0的情况。上面的类型无法满足,所以后面个函数重载约束没有传入初始值的实现。- function useState<S=undefined>(): [S|undefined, Dispatch<SetStateAction<S|undefined>>];
复制代码
useRef
定义比较简单:- function useRef<T>(initialValue:T):MutableRefObject<T>
复制代码 ,返回一个可变 ref 对象,其属性初始化为传递的参数。就是一个包含的接口。值得注意的是 这里同样用了函数重载,包括了没有传或者为null的情况。在中大部分的初始值都为。 类型声明中注释明确指定了如果要使用可变的则需要在范型参数中包含.- * Usage note: if you need the result of useRef to be directly mutable, include `| null` in the type
- * of the generic argument.
复制代码 如果我们这样写,此时ref为类型的被修饰。所以是不可变的。当在范型中指定了则根据函数重载命中第一种类型,返回是可变的。- const ref = useRef<number>(null)
- ref.current = 2 // 无法分配到 "current" ,因为它是只读属性。
- // 此时命中的这个重载的useRef
- function useRef<T>(initialValue: T|null): RefObject<T>;
复制代码 useEffect
定义:- function useEffect(effect:EffectCallback, deps?:DependencyList):void
复制代码 ,是一个只能返回的函数类型 用来处理副作用 。表示没有返回值 ,但这里并不意味着你赋值一个有返回值的函数会报错,在一个返回值为的函数你明确返回类型 并不会报错。而void真正表示无论你返回什么?编译器都不会使用检查它。表示析构函数,看下它的定义- declare const UNDEFINED_VOID_ONLY: unique symbol;
- type Destructor = () => void | { [UNDEFINED_VOID_ONLY]: never }
复制代码 这里表示一个常量类型是的子类型 , 使用的变量必须为,而值为表示的是那些永不存在的值的类型。类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型。这里使用和{- [UNDEFINED_VOID_ONLY]: never }
复制代码 作为联合类型, 明确约束了是不能有返回类型的, 如果明确声明 则会报错。 如果有修饰函数默认返回类型, 所以在中的也同样不能使用。是可选参数,作为依赖是一个只读数组。是一个真正的只读数组类型,根据范型来约束数组元素类型。它没有改变数组的方法等。
useLayoutEffect
类型声明与一致。但的会在更新后同步触发 在浏览器同步刷新之前执行完成 可能会阻塞浏览器渲染。
useReducer
官方介绍为useState是的替代解决方案。一般我们都这样使用。当结构或逻辑比较复杂时,用管理更方便容易。- function reducer(state, action) {
- switch (action.type) {
- case 'increment':
- return {count: state.count + 1};
- case 'decrement':
- return {count: state.count - 1};
- default:
- throw new Error();
- }
- }
- const [state, dispatch] = useReducer(reducer, {count: 0});
- state.count
- dispatch({type: 'decrement'})
复制代码 在类型声明文件中写了5个重载函数类型。- type ReducerWithoutAction<S> = (prevState: S) => S;
- type ReducerStateWithoutAction<R extends ReducerWithoutAction<any>> =
- R extends ReducerWithoutAction<infer S> ? S : never;
- function useReducer<R extends ReducerWithoutAction<any>, I>(
- reducer: R,
- initializerArg: I,
- initializer: (arg: I) => ReducerStateWithoutAction<R>
- ): [ReducerStateWithoutAction<R>, DispatchWithoutAction];
复制代码
- 第一种是函数没有传的情况。R表示, 其中类型和返回类型必须一致。表示初始参数,类型为泛型的第二个参数。定义稍微复杂,但是其实约束了此类型必须是一个参数为类型 返回值也同类型一致的参数类型。而这个就是的参数类型。
- ReducerStateWithoutAction
复制代码 就是为了约束这三个参数的类型。举个例子更清晰. 下述代码中已经的参数和返回参数类型都应该保持一致。
- type stateType = {num: number}
- function reducer(state: stateType) {
- return state
- }
- const [state,dispatch]=useReducer<typeof reducer,stateType>(
- reducer, {num: 0},state=>{
- return {num: state.num+1}
- })
复制代码 这里的条件类型是一种条件表达式进行类型的关系检测,类似于三元表达式。意思为左侧类型可分配给右侧类型则返回?后面的类型 否则返回:后的类型。 而关键字只能出现在条件类型判断为的分支,表示一个待推断的类型,表示将推断的类型保存在中。
- 第二个重载与第一个类似 只是在为的情况。如果在的泛型中指定了第二个参数,则命中第一个重载 此时会报错。具体实现类似下述代码。
- function useReducer<R extends ReducerWithoutAction<any>>(
- reducer: R,
- initializerArg: ReducerStateWithoutAction<R>,
- initializer?: undefined
- ): [ReducerStateWithoutAction<R>, DispatchWithoutAction];
- type stateType = {num: number}
- function reducer(state: stateType) {
- return state
- }
- const [state,dispatch]=useReducer<typeof reducer>(
- reducer, {num: 0})
复制代码
- 第三个重载约束了函数传入的情况,不同于是类型。initializerArg初始参数为与泛型的交叉类型。可能是的子集的情况。同样是为了取出中类型。同上述第一种重载类似。要约束一致。而初始的返回值要与中一致。
- // Unlike redux, the actions _can_ be anything
- type Reducer<S, A> = (prevState: S, action: A) => S;
- // types used to try and prevent the compiler from reducing S
- // to a supertype common with the second argument to useReducer()
- type ReducerState<R extends Reducer<any, any>> = R extends Reducer<infer S, any> ? S : never;
- function useReducer<R extends Reducer<any, any>, I>(
- reducer: R,
- initializerArg: I & ReducerState<R>,
- initializer: (arg: I & ReducerState<R>) => ReducerState<R>
- ): [ReducerState<R>, Dispatch<ReducerAction<R>>];
复制代码 举个例子 初始参数的类型 在初始函数的参数类型也应该一致。- // 代码实现
- type stateType = {num: number}
- type actionType = { type: string, payload: number}
- function reducer(state: stateType,action: actionType) {
- if(action.type=='add'){
- return {num: state.num+1}
- }else {
- return {num: state.num-1}
- }
- }
- const [state,dispatch]=useReducer<typeof reducer,actionType>(
- reducer, { type: 'add', payload: 1,num: 2},state=>{
- return {num:state.num+state.payload}
- })
复制代码
- 第4个重载 和第三个类似 在初始参数不包括state的情况, 初始参数的类型 在初始函数的参数类型也应该一致。
- function useReducer<R extends Reducer<any, any>, I>(
- reducer: R,
- initializerArg: I,
- initializer: (arg: I) => ReducerState<R>
- ): [ReducerState<R>, Dispatch<ReducerAction<R>>];
复制代码 第5个重载 和上述类似 约束了为,存在的情况- function useReducer<R extends Reducer<any, any>>(
- reducer: R,
- initialState: ReducerState<R>,
- initializer?: undefined
- ): [ReducerState<R>, Dispatch<ReducerAction<R>>];
复制代码 的返回值都是一致。返回和,而- type Dispatch<A> = (value:A) =>void;
复制代码 就是一个没有返回值的函数 用来触发改变。
useCallback
定义比较简单:- function useCallback<T extends (...args:any[]) =>any>(callback:T, deps:DependencyList):T;
复制代码 范型为类型为第一个参数的类型,第二个参数与的依赖数组一致,都是一个只读的数组。主要作用是用来缓存实例,当传递给子组件方法时与React.memo 或者shouldComponentUpdate一起使用。
useMemo
定义也比较简单:- // allow undefined, but don't make it optional as that is very likely a mistake
- function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;
复制代码 范型为的返回值类型。依赖为和的联合类型,这里会有提示允许为,但不能是可选的 否则可能是个错误。
useImperativeHandle
主要用来配合自定义暴露给父组件数据的。一般用来父组件调用子组件方法或获取子组件数据时使用。- function useImperativeHandle<T, R extends T>(ref: Ref<T>|undefined, init: () => R, deps?: DependencyList): void;
- interface RefObject<T> {
- readonly current: T | null;
- }
- // Bivariance hack for consistent unsoundness with RefObject
- type RefCallback<T> = { bivarianceHack(instance: T | null): void }["bivarianceHack"];
- type Ref<T> = RefCallback<T> | RefObject<T> | null;
复制代码 泛型为的的类型,是第二个参数方法的返回值,同上述依赖数组一样 不可变数组。可以这样使用- const Child = React.forwardRef<{num: number}>((prop,ref)=>{
- useImperativeHandle<{num: number}, {num: number}>(ref,()=>({
- 'num': 1
- }))
- return (<div>123</div>)
- })
- const Foo = ()=>{
- const childRef = useRef<{num: number}|null>(null)
- useLayoutEffect(() => {
- console.log(childRef.current?.num) // 1
- }, [])
- return <>
- <Child ref={childRef}/>
- </>
- }
复制代码 总结
本文根据阅读@types/react下相关源码入手,意在帮助大家熟悉常用hook以及类型声明 在开发时能得心应手 明白hooks的约束条件 更深入理解的功能。如上述内容有错误,请不吝指出
以上就是一文带你搞懂react hooks的类型声明的详细内容,更多关于react hooks的类型声明的资料请关注脚本之家其它相关文章!
来源:https://www.jb51.net/javascript/291963xuk.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
|