共计 3746 个字符,预计需要花费 10 分钟才能阅读完成。
枚举类型怎么定义?
有这么一个常量FieldTypes
,代表表单项类型,是输入框还是开关还是其余等等,只列举两个
enum FieldTypes {
INPUT = 'input',
SWITCH = 'switch'
// ...
}
此时子组件须要接管一个 type
的props
,类型的值为 FieldTypes
中的定义的值,即 'input' | 'switch'
等等
解法一:
typescrip
t 中 enum
能够作为类型束缚某个变量,如果这么做,那么给这个 props
的type
赋值时,必须从 enum
中去取,保障了数据的起源以及数据类型的统一性,毛病是在某些场景下不是特地灵便
const _type: FieldTypes = FieldTypes.INPUT // ✅ correct
const _type: FieldTypes = 'input' // ❎ error
解法二:
应用只读的 object
来代替enum
的作用
const FieldTypesObj = {
INPUT: 'input',
SWITCH: 'switch'
} as const // <-- 要害技巧 1: as const
type Type = typeof FieldTypesObj // <-- 要害技巧 2: 用 typeof 关键字从 FieldTypesObj 反向创立同名类型
const _type1: Type[keyof Type] = 'input' // ✅ correct
const _type2: Type[keyof Type] = FieldTypes.SWITCH // ✅ correct
常量断言(语法写作 as const
)是 TypeScript 3.4 公布的新个性,这里对它进行简略的解释:
先看上面例子:
let str = 'ghostwang' // str: string
const str = 'ghostwang' // str: 'ghostwang'
let str = 'ghostwang' as const // str: 'ghostwang'
const obj1 = {name: 'ghostwang'} // const obj: {name: string;}
const obj2 = {name: 'ghostwang'} as const // const obj: {readonly name: "ghostwang";}
const arr = [1, 2] // const arr: number[]
const arr2 = [1, 2] as const // const arr: readonly [1, 2]
看出区别来了么,应用 as const
会通知编译器为表达式推断出它能推断出的 最窄或最特定 的类型。如果不应用它,编译器将应用其默认类型推断行为,这可能会导致更宽泛或更个别的类型,并且此时他的属性是只读的(当然还是有方法能批改)
事件类型该怎么定义?
置信有人在定义事件的时候有时候不晓得怎么去定义,例如上面的场景,div 点击时阻止冒泡
const onClick = (e)=>{// 这里 e 的类型是什么?e...//
}
<div onClick={onClick}><div>
这里很多会定义为e:any
而后冒泡的函数是啥,阻止冒泡 stop 什么什么,再去查一下。
其实想一想,如果能晓得 <div>
的 props 的类型定义,我能够间接定义 onClick 这个函数,从而 e 就有类型了,不仅能够查看代码,还能够失去敌对的提醒。JSX 提供了这样一个查问组件 props 的泛型:
// 取得 div 标签的 props 的类型 (内置组件,例如 div、a、p、input 等等)
const DivPropsType = JSX.IntrinsicElements<'div'>
// 取得自定义组件的 props
const CustomPropsType = JSXElementConstructor<CustomComponent>
为了缩小记忆的累赘,React 对这 2 个泛型又进行了一步包装:
type ComponentProps<T extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>> =
T extends JSXElementConstructor<infer P>
? P
: T extends keyof JSX.IntrinsicElements
? JSX.IntrinsicElements[T]
: {};
所以下面的事件类型定义就变得非常简单了:
import {ComponentProps} from 'react'
const onClick: ComponentProps<'div'>['onClick'] = (e)=>{// e 的类型被主动推导
e.// 会失去代码提醒
}
<div onClick={onClick}><div>
同样,咱们在引入自定义组件时,也不须要独自引入它的 props 类型,间接应用这个泛型即可:
import {type ComponentProps} from 'react'
const onClick: ComponentProps<typeof Custom>['onClick'] = (e)=>{// e 的类型被主动推导
e.// 会失去代码提醒
}
<Custom onClick={onClick}></Custom>
怎么定义一个字符串类型,既有枚举,又能够自在输出?
这种利用场景十分常见,我这样写题目,可能表白得不清晰,举一个例子:
我须要定义一个 color 类型,在开发者输出时,能够提醒输出 “red” 和 “blue”,然而除了 red 和 blue 也能够自在输出其余字符串
然而这样定义类型的话,除了 red 和 blue,其余都输出不了。都会报错。
如果加上 string,间接什么都不提醒了
所以须要定义一个既蕴含 red 和 blue,又蕴含除了 red 和 blue 之外的字符串
我输出了 white,也不会报错
ref 的类型该怎么定义?
ref 的利用场景罕用来贮存一些扭转不会引起从新渲染、用来援用 forwardRef 的组件、援用内置组件应用。
import {useRef} form 'react'
const valueRef = useRef<number>(0)
这种形式定义的 valueRef 的类型是MutableRefObject
可变的援用对象
除了这种形式,还有一种不可变的,对应的类型是RefObject
只读的援用对象 感觉这俩就是 const 和 let 一样
看一下区别
import {useRef, type MutableRefObject, type RefObject} form 'react'
const valueRef1: MutableRefObject<number> = useRef(0)
const valueRef2: RefObject<number> = useRef(0)
valueRef1.current = 1; // 失常
valueRef2.current = 1; // 报错,不能赋值:无奈调配到 "current",因为它是只读属性。
所以咱们在定义几种场景时,应辨别是手动赋值还是主动赋值,并应用不同的类型
例如用来封装一个 useUUID
的 hook
import {useRef, type RefObject} form 'react'
// 定义只读的 ref
export const useUUID = ()=>{const uuidRef: RefObject<string> = useRef('uuid' + Date.now().toString(16));
return uuidRef.current
}
例如援用一个 div 的 ref
import {useRef, type RefObject} form 'react'
const divRef: RefObject<HTMLDivElement> = useRef();
<div ref={divRef}></div>
forwardRef 的类型该怎么定义以及援用时类型该怎么定义?
依据官网的举荐,在定义 forwardRef 时,将类型定义在高阶函数中(留神⚠️props 的类型和 ref 类型地位相同)
const ComA = forwardRef<{dddd: string}, {age?: number}>((props, ref)=>{useImperativeHandle(ref, ()=>{
return {dddd: "1"}
})
return <div></div>
})
在引入时,typeof ComA
失去的是一个 ref 和 props 的穿插类型,所以只需拜访出 ref 的类型即可
const ComB = ()=>{const tRef: ComponentProps<typeof ComA>['ref'] = useRef();
return <ComA ref={tRef}></ComA>
}
React 在此处设计的类型是 ComponentProps<typeof ComA>['ref']
返回的是一个React.Ref
泛型
type Ref<T> = RefCallback<T> | RefObject<T> | null;
正好兼容了 ref 的 3 种状况,应用函数接管(createRef),useRef 援用,和初始空值。而且还是个只读的 ref 👍
而后拜访 tRef 时,此时 tRef 即是曾经收窄的类型,具备敌对的提醒和取值限度。
写在最初的话
至此,联合最近组内小伙伴分享的一些 ts 类型应用技巧,在此总结并分享给更多的人,感激浏览~