資料整理
例如有兩個 interface 屬性一樣,差異在於 型別內容
1 2 3 4 5 6 7 8 9 10
| interface DataA { key:string key2:number }
interface DataB { key:string key2:boolean }
|
- 使用 泛型 整理
- 將 T 部分,設為傳入的變數,可以依據宣告得變數所符合的型別進行設定
1 2 3 4 5 6 7 8
| type GenericsObj<T> = { key:string key2:T } type DataA = GenericsObj<number>; type DataB = GenericsObj<boolean>;
|
範例2:
1 2 3 4 5 6 7 8 9 10
| interface KeyPair<T, U> { key: T; value: U; }
let kp1: KeyPair<number, string> = { key: 1, value: "str"} let kp2: KeyPair<string, number> = { key: "str", value: 123}
let arr:number[] = [1,2,3]; let arrTwo:Array<number> = [1,2,3]
|
payload 應用
- 這邊以 redux 中整理的 action 為範例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| type userDataPayload = { userId: string, data:any }
const setUserDataAction = (payload:userDataPayload )=>({ type: 'SET_USER_DATA', payload }) const resetUserDataAction =()=>({ type: 'RESET_USER_DATA', })
type CartDataPayload = { product: string } const setCartDataAction =(payload:CartDataPayload)=>({ type: 'SET_CART_DATA', payload })
type SetUserDataAction = { type: 'SET_USER_DATA', payload:userDataPayload } type ResetUserDataAction = { type: 'RESET_USER_DATA' } type SetCartDataAction = { type: 'SET_CART_DATA', payload:CartDataPayload }
|
- 由上可以觀察到 SetUserDataAction、ResetUserDataAction、SetCartDataAction 整理為一個
- 將 action 的型別整理,其key為type 與 payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| type Action<T,P> = { type: T, payload:P } type ActionWithoutPayload<T> = { type: T }
type SetUserDataAction = Action<'SET_USER_DATA',userDataPayload> type SetCartDataAction = Action<'SET_CART_DATA',CartDataPayload> type ResetUserDataAction = ActionWithoutPayload<'RESET_USER_DATA'>
type Actions = SET_USER_DATA | SET_CART_DATA | RESET_USER_DATA;
|
- 以下,進一步再將 Action 和 ActionWithoutPayload 進行整合,他們差異在與 payload
1
| type Action<T, P = null> = p extends {} ? {type: T, payload: P} : {type: T};
|
型別參數動態生成不同型別
函式的泛型
1 2 3
| const fn = (param: string | number):(string|number)[]=>[param]; fn(1); fn('hello')
|
1 2 3
| const fn = <T>(param: T):(T)[]=>[param]; fn(1); fn('hello')
|
1 2 3
| function fn<T>(param:T):T[]{ return [param] }
|
type 的泛型
1 2 3 4 5 6 7 8 9 10 11 12 13
| type GenericList<T> = T[];
type StrList = GenericList<string>; type BoolList = GenericList<boolean>;
type GenericValObj<T> = {[key:string]:T} type NumValObj = GenericValObj<number>
const numValObj:NumValObj = { img_res_200:200 }
|
1 2 3 4 5
| type GenericUnion<T,U> = T | U | T[] | U[]; type strNumUnion = GenericUnion<string,number>
const val:strNumUnion = "123" const val2:strNumUnion = ["123"]
|
將函式改寫為 共用的泛型型別
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const fn = <T>(param: T):(T)[]=>{ return [param] };
interface Props { onFn: typeof fn }
type GenericFn = <T>(param: T) => T[] const fn:GenericFn = (param) => { return [param] }
interface Props { onFn:GenericFn }
|
1 2 3 4 5 6
| function fn<T>(param:T):T[]{ return [param] }
type FnType = typeof fn ;
|
比較<T>
位置差異
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| type FnTypeA = <T>(param: T) => T[] type FnTypeB<T> = (param: T) => T[]
const fn1:FnTypeA = (param) => { return [param] }
const fn2:FnTypeB<string> = (param) => { return [param] }
fn1();
fn2('Hi')
|
- fn1 可以填入任何內容、可以動態填入型別
- fn2 只能填入 string 內容
1 2 3 4 5 6 7
| export type ClickFn = <Event>(e:Event) => any
const handleClick:ClickFn = (e)=>{
}
|
interface 的泛型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| export interface GenericI<T>{ [key:string]: T } type NumValObj = GenericI<number>; const numberValObj:NumValObj = { res:123 }
export type GenericT<T> = { [key:string]: T }
export type GenericKeyValObj<T extends keyof any,P> = { [key in T ]: P }
type ApiKey = 'user' | 'id' | 'password'; type UserApiData = GenericKeyValObj<ApiKey, string>
|
可以很快的生成 API 的資料型別
其他範例
1 2 3 4 5 6 7 8 9 10 11
|
type GenericApiFn<Params, Res> = (params:Params)=> Res
interface ApiContainerProps<Params, Res> { initData: Res; onAsyncCb: GenericApiFn<Params, Res> }
|
1 2 3 4 5 6 7 8
| interface FnI { <T>(param: T): T[] }
type FnT = <T>(param:T)=>T[]
const fn:FnT = (param)=>[param]
|
1 2 3 4 5 6
| interface GenericFnI<T> { (param: T): T[] }
const fn2: GenericFnI<string>= (param)=>[param]
|
過去都未曾注意到泛型可以抽成共用還有 <T>
的位置所放的位置,會影響宣告,透過此次的教學,讓我對泛型可以應用於實際情況的案例,同樣未來在訂定 type 或 interface 時可以加以注意,思考是否可以改寫為泛型加以使用。
參考資料
成為進階TS開發者的第一哩路 — 泛型簡介與基礎(1)
成為進階TS開發者的第一哩路 — 泛型的函式, type和interface寫法一次說清楚!