{%hackmd BJrTq20hE %} ###### tags: `TypeScript` # TypeScript Generics(泛型) ## 什麼是泛型 泛型在函式裡可以讓函式在使用的時候再決定參數、回傳的資料類型。 ### 函式參數的泛型 ```javascript= function hello<T>(data:T){ console.log(data) } hello<string>("Jack")//Jack ``` ### 函式return的泛型 ```javascript= function hello<T>(data:T):T{ return data } console.log(hello("Jack"))//Jack ``` 泛型的適用範圍就只有在該函式上,所以不同的函式相同的泛型名稱是可以的。 可以看到下方的範例程式碼泛型都是T,不過在實際使用時不會互相影響,因為他們的作用範圍只在函式上。 ```javascript= function hello<T>(data:T){ console.log(data) } function pluse<T>(par1:T, par2:T){ console.log(par1 , par2) } hello<string>("Jack")//Jack pluse<number>(5 , 6)//5 6 ``` ### 如果把上述的par1 + par2則會報錯 ```javascript= function pluse<T>(par1:T, par2:T){ console.log(par1 + par2) } hello<string>("Jack")//Jack pluse<number>(5 , 6) ```  猜測原因是泛型會避免所傳入的型別參數無法做運算而報錯,所以先報錯。 ### 泛型也可以用上type ```javascript= type Obj99 = { name:string, age:number} function hello<T>(data:T):T{ console.log(data) return data } const data:Obj99 = { name:"Jack", age:26} hello<Obj99>(data)// { name:"Jack", age:26} ``` ## function 與 泛型 下方的範例程式碼在說明interface與泛型在function中的使用 ```javascript= // interface的泛型決定isWork的資料類別 interface UserInterface<T> { name: string age: number isWork:T } // function test的泛型決定了所傳入參數的類別之外,也決定了 // return的資料型別為interface UserInterface,又因為 // interface有泛型所以isWork的資料類別也會跟著決定 function test<U>(data:U):UserInterface<U>{ const userData:UserInterface<U> = { name: "Jack", age: 54, isWork:data } return userData } console.log(test<boolean>(true));//{name: 'Jack', age: 54, isWork: true} ``` ## class 與 泛型 下方的範例程式碼在說明interface與泛型在class中的使用 class 的泛型在創建實體的時後決定泛型的類型,如果interface也有泛型,也會在同時決定。 ```javascript= interface CarInterface<T> { carName: string carNumber: number carModel: T } class Car<U> implements CarInterface<U>{ carName: string carNumber: number carModel: U constructor(name: string, number: number, model: U) { this.carName = name this.carNumber = number this.carModel = model } } const myCar = new Car<string>("the new type car", 12345, "fast type") console.log(`carName:${myCar.carName},carNumber${myCar.carNumber},carModel ${myCar.carModel}`) //carName:the new type car,carNumber12345,carModel fast type ``` ## TypeScript Generics - 進階應用 - 什麼是extends條件判斷? extends除了可以用在interface 與 class中,也可以用在泛型的型態判斷 interface與class的extends範例 ```javascript= interface UerInterface{ name:string age:number } interface JackInterface extends UerInterface{ } const JaceData:JackInterface = { name:"Jack", age:7 } class User{ name:string = "Jack" } class Jack extends User{ } const jack = new Jack() console.log(jack.name)//Jack ``` ### 判斷條件的基礎寫法 下方的程式碼是type T的類型判斷,string能不能兼容string可以的話type T的類型就是string不是的話就是number ```javascript= type T = string extends string ? string : number ``` ### interface 與 interface間的比較 interface UserB可兼容interface UserA所以type U是string類別 ```javascript= interface UserA{ name:string } interface UserB{ name:string age:number } type U = UserB extends UserA ? string : number ``` ### union 下方的type U T都是number因為union類不會包含union內的各個資料類別,可以看到下圖type U與type T都是數字類別,因為union沒有包含number或是'jack'。 ```javascript= type U = string | number extends number ? string : number type T = 'Jack' | 'jack' extends 'jack' ? string : number ```   ### nuion與泛型 會一個一個傳入比較 ```javascript= type DemoU<U> = U extends number ? string : number type DemoUU = DemoU<string | number> type DemoT<T> = T extends 'jack' ? string : number type DemoTT = DemoT<'Jack'|'jack'> ```   ### never never被視為所有資料類型的子集,結果都會是true ```javascript= type DemoU = never extends number ? string : number ```  ### never與泛型 但是如果never在泛型中的話會被看作是空的union也是會一個一的帶入,但是帶入的是空值,所以Result也會被當作不會有任何的類型return ```javascript= type DemoT<T> = T extends number ? string : number type Result = DemoT<never> ```  在泛型中never被視為空,所以類別的判斷不會回傳任何東西 ### 如何解決nuion與泛型一個一個傳入比較的問題 使用[]表示要一起把union傳入 ```javascript= type DemoU<U> = [U] extends [string|number] ? string : number type DemoUU = DemoU<string | number> ```  要比較的泛型(extends後)也是要放在[] ```javascript= type DemoU<U> = [U] extends string|number ? string : number type DemoUU = DemoU<string | number> ```  ## function中要傳入Array類型的參數要怎麼辦? 在一般的function與泛型使用範例如下 但是這個時候會報錯,因為不知道T是不是真的為Array所以報錯 ```javascript= function doSomethingGood<T>(par:T){ console.log(par.length); } ```  把泛型extend為Arrayy,表示先跟TS說泛型T是Array ```javascript= function doSomethingGood<T extends Array<T>>(par:T){ console.log(par.length); } ```  ## infer 可以想成是類型的儲存地,當滿足extends的包容時,才會對infer的內別進行判斷 ### infer 與 Array 以下的程式碼表示泛型T需要為Array,如果是Array則會再另外判斷Array內的資料類別並儲存在P上,如果不是Array則為never。 type R1 的類型為string 因為Array內的資料為string type R2 的類型為never ```javascript= type DemoT<T> = T extends Array<infer P> ? P: never type R1 = DemoT<['Jack']> type R2 = DemoT<123> ``` ### infer也可以用在定義function參數所傳入的類別 與 function infer也可以用在定義function參數所傳入的類別 下方的程式碼表示type FnPar的泛型T只能是有一個參數的function 而type FR會依據type FnPar的泛型來決定型別 ```javascript= type FnPar<T> = T extends (par: infer P) => any ? P : never type FR = FnPar<(data:number)=>void> ```  把傳入的參數多加了123則type FR的類別會變為never ```javascript= type FnPar<T> = T extends (par: infer P) => any ? P : never type FR = FnPar<(data:number,123)=>void> ```  #### infer 在 function 中與interface一起使用 下方的程式碼表示type FnPar的泛型T只能是有一個參數的function 而type FR會依據type FnPar的泛型來決定型別,這個型別也可以是interface ```javascript= type FnPar<T> = T extends (par: infer P) => any ? P : never interface UserInfoInterface {name : string} type FR2 = FnPar<(data:UserInfoInterface)=>void> ```  #### 如果泛型不是函式會怎麼樣? T extends不包含函示所以直接判斷T為never ```javascript= type FnPar<T> = T extends (par: infer P) => any ? P : never interface UserInfoInterface {name : string} type FR3 = FnPar<[]> ```  ## keyof keyof的功能 1.把interface的key一一的提出來,可以想成是轉換為union(不是sting,number那一種的) 2.在function的泛型告知TS,key的來源 ### 轉換為union ```javascript= interface UserData { name:string age:number job:string location:string } type UserType = keyof UserData // 這裡可以把UserType看為是 'name' | 'age' | 'job'| 'location' 這種union let dataName:UserType = 'name' let dataAge:UserType = 'age' let dataJob:UserType = 'job' let dataLocation:UserType = 'location' // 這個會報錯,因為沒有出現在interface中 let dat:UserType = 'jack' ```  ### 在function的泛型告知TS,key的來源 以下的程式碼是很常見的傳入一個物件並且回傳出資料的function,不過以下的寫法TS會認為你該回傳的是Array而報錯 ```javascript= function getData<T,K>(obj:T, key:K):T[K] { return obj[key] } ```  修改為如下 ```javascript= function getData<T,K extends keyof T>(obj:T, key:K):T[K] { return obj[key] } ``` 簡單來說就是跟TS說K是T的key
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.