列舉型別怎麼定義?
有這麼一個常量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型別使用技巧,在此總結並分享給更多的人,感謝閱讀~