TypeScript 函數(shù)
函數(shù)是任何應(yīng)用程序的基本構(gòu)建塊,無(wú)論它們是本地函數(shù)褒繁、從另一個(gè)模塊導(dǎo)入的函數(shù)亦鳞,還是類中的方法。它們也是值棒坏,就像其他值一樣燕差,TypeScript 有很多方法來(lái)描述如何調(diào)用函數(shù)。
了解編寫(xiě)函數(shù)類型之前, 先了解一下如何為函數(shù)參數(shù)以及函數(shù)返回值添加類型注釋坝冕。
1. 函數(shù)的參數(shù)類型注釋
函數(shù)是在JavaScript中傳遞數(shù)據(jù)的主要方式.TypeScript 允許您指定函數(shù)的輸入和輸出值的類型徒探。
聲明函數(shù)時(shí),可以在每個(gè)參數(shù)后面添加類型注釋, 聲明函數(shù)接受那些類型的參數(shù).參數(shù)類型注釋在參數(shù)名稱:
之后
例如:
// 帶有參數(shù)類型的函數(shù)
function greet(name:string){
console.log(`hello ${name.toUpperCase()}`)
// (parameter) name: string
}
當(dāng)參數(shù)具有類型注釋時(shí), 將檢查該函數(shù)參數(shù)的類型, 參數(shù)類型不匹配將報(bào)錯(cuò)
例如
greet(20)
// 報(bào)錯(cuò): 類型“number”的參數(shù)不能賦給類型“string”的參數(shù)。
2. 返回類型注釋
除了可以給函數(shù)參數(shù)添加類型注釋, 還可以給函數(shù)的返回值添加類型注釋.
返回的類型注釋出現(xiàn)在參數(shù)列表之后
function greet():number {
return 30
}
與變量類型注釋非常相似, 函數(shù)通常不需要返回類型注釋, 因?yàn)門(mén)ypeScript會(huì)更加函數(shù)的返回值推斷出函數(shù)的類型.
而一些代碼庫(kù)中明確指定返回類型以用于文檔為目的, 防止意外更改, 在這可能就處于個(gè)人喜好.
那么接下來(lái)就讓我們了解如何編寫(xiě)函數(shù)的類型
3. 函數(shù)類型表達(dá)式
描述函數(shù)最簡(jiǎn)單的方式就是使用函數(shù)類型表達(dá)式. 類型在語(yǔ)法上有點(diǎn)類似于箭頭函數(shù)
例如:
// 變量添加函數(shù)類型的類型注釋
let myFn:(name:string) => number;
// let myFn: (name: string) => number
/*
函數(shù)類型注釋表面myFn變量將是一個(gè)函數(shù)類型
而且還是一個(gè)接受string類型參數(shù), 并返回number類型的函數(shù)
*/
// ok
myFn = (age:string) => {
return Math.random()
}
// 雖然參數(shù)命名不一樣, 但是參數(shù)類型符合類型注釋
// 參數(shù)類型不符
myFn = (name:number) =>{
return 26
}
/*
報(bào)錯(cuò)
不能將類型“(name: number) => number”分配給類型“(name: string) => number”喂窟。
參數(shù)“name”和“name” 的類型不兼容测暗。
不能將類型“string”分配給類型“number”。
*/
// 返回值類型不符
myFn = (age:string) => {
return 'hello'
}
/*
報(bào)錯(cuò):
不能將類型“(age: string) => string”分配給類型“(name: string) => number”磨澡。
不能將類型“string”分配給類型“number”碗啄。
*/
當(dāng)然也可以聲明變量時(shí)直接初始賦值,只不過(guò)會(huì)比較長(zhǎng)
let myFn:(name:string) => number = (s:string) => {
return 30
}
要注意區(qū)分,哪里到哪里是函數(shù)類型, 哪里到哪里是真實(shí)的函數(shù)值
也可以給函數(shù)參數(shù)添加函數(shù)類型的類型注釋
// 函數(shù)參數(shù)添加函數(shù)類型的類型注釋
function greeter(fn: (x:string) => void){
fn('hello world')
}
// 執(zhí)行函數(shù)
// 作為參數(shù)使用的函數(shù)比喻符合參數(shù)類型
function print(s:string){
console.log(s)
}
// 執(zhí)行函數(shù)
greeter(print)
示例中,參數(shù)fn
的類型是一個(gè)(x:string)=> void
, 表示fn
類型是帶有一個(gè)string
類型參數(shù),沒(méi)有返回值的函數(shù).
就像函數(shù)聲明一樣, 如果函數(shù)參數(shù)沒(méi)有指定類型, 則參數(shù)是隱式的any
類型
function example(s){
console.log(s)
// (parameter) s: any
}
還有一點(diǎn)需要注意的是, 函數(shù)類型 參數(shù)必須有參數(shù)名
例如:如下函數(shù)類型不加參數(shù)名的情況
function greeter(fn: (string) => void){
// (parameter) string: any
fn('hello world')
}
說(shuō)明:
-
(s:string) =void
函數(shù)類型表示接受一個(gè)string類型參數(shù), 無(wú)返回值 -
(string) => void
函數(shù)類型中,string
被解析為類型名稱,而不是類型, 表示函數(shù)接受一個(gè)默認(rèn)any
類型的參數(shù),無(wú)返回值
4. 函數(shù)類型推斷
跟變量類型注釋一樣, 有的是有我們并不需要明確的指定類型, 因?yàn)門(mén)ypeScript會(huì)更加我們賦予的值推斷出類型
函數(shù)類型也是一樣.
例如:
// 1.變量沒(méi)有添加類型注釋, TypeScript根據(jù)賦值函數(shù)類型推斷
let myAdd = function (x:number, y: number):number {
return x + y
}
// let myAdd: (x: number, y: number) => number
// 2. 變量添加函數(shù)類型注釋
let myAdd2: (value1: number, value2: number) => number = function (x, y){
return x + y
}
// let myAdd2: (value1: number, value2: number) => number
示例中: 兩個(gè)函數(shù)表達(dá)式的方式定義的函數(shù), 一個(gè)添加了類注釋,一個(gè)沒(méi)有添加類型注釋,但是變量的類型是完全相同的.
5. 函數(shù)類型別名
5.1 使用類型別名定義函數(shù)類型
當(dāng)然,直接通過(guò)使用函數(shù)類型來(lái)進(jìn)行類型注釋,復(fù)用性不高, 因此可以將函數(shù)類型抽離使用類型別名,或通話簽名
// 類型別名
type GreetFunction = (s:string) => void
// 函數(shù)參數(shù)添加類型注釋
function greeter(fn:GreetFunction){
fn('hello world')
}
5.2 調(diào)用簽名
在JavaScript中, 函數(shù)除了可調(diào)用之外還可以具有屬性. 但是,函數(shù)表達(dá)式語(yǔ)法不允許聲明函數(shù)屬性.
如果像使用屬性描述可調(diào)用的函數(shù), 我們可以在對(duì)象類型中編寫(xiě)調(diào)用簽名
例如:
// 類型別名(通過(guò)調(diào)用簽名聲明函數(shù)類型)
type GreetFunction = {
(s:string): void
}
// 函數(shù)參數(shù)類型為函數(shù)類型
function greeter(fn:GreetFunction){
fn('hello world')
}
請(qǐng)注意,調(diào)用簽名與函數(shù)表達(dá)式相比,語(yǔ)法略有不同, 參數(shù)列表和返回返回類型之間的=>
修改為對(duì)象類型屬性和值類型中中鍵的:
號(hào)
除了調(diào)用簽名,還可以聲明函數(shù)上其他的屬性類型
// 類型別名(通過(guò)調(diào)用簽名聲明函數(shù)類型)
type GreetFunction = {
descript: string // 函數(shù)屬性
(s:string): void
}
// 函數(shù)參數(shù)類型為函數(shù)類型
function greeter(fn:GreetFunction){
console.log(`${fn('hello')} ${fn.descript}`)
}
// 作為參數(shù)的函數(shù)
function example(x:string){
return x
}
// 調(diào)用
greeter(example)
/*
報(bào)錯(cuò):
類型“(x: string) => string”的參數(shù)不能賦給類型“GreetFunction”的參數(shù)。
類型 "(x: string) => string" 中缺少屬性 "descript"钱贯,但類型 "GreetFunction" 中需要該屬性挫掏。
*/
示例報(bào)錯(cuò)的原因在于參數(shù)類型中的函數(shù)有descript
屬性, 但是我們傳入的函數(shù)沒(méi)有此屬性
給參數(shù)函數(shù)添加屬性,重新調(diào)用
// 作為參數(shù)的函數(shù)
function example(x:string){
return x
}
example.descript = 'world'
// 調(diào)用
greeter(example)
此時(shí)代碼就沒(méi)有任何報(bào)錯(cuò)了
除了可以使用類型別名來(lái)定義帶有調(diào)用簽名的函數(shù)類型, 接口也可以實(shí)現(xiàn)相同的功能
例如:
// 接口聲明帶有調(diào)用簽名的對(duì)象類型
interface GreetFunction {
descript: string
(s:string): void
}
// 函數(shù)參數(shù)類型為函數(shù)類型
function greeter(fn:GreetFunction){
console.log(`${fn('hello')} ${fn.descript}`)
}
其實(shí)所謂的調(diào)用簽名本事上就是一個(gè)對(duì)象類型, 其中有一個(gè)屬性為調(diào)用簽名,表示可以像函數(shù)一樣調(diào)用, 除此之外還可以定義其他屬性
需要注意的時(shí), 函數(shù)參數(shù)的名稱不必與調(diào)用簽名中參數(shù)名一致, 只要對(duì)應(yīng)的位置類型匹配即可
// 定義檢測(cè)函數(shù)類型的接口
interface MyFunction{
// 調(diào)用簽名:形參第一個(gè)為string類型,第二個(gè)為number類型, 函數(shù)返回boolean類型
(params: string, subString:number):boolean
}
// 類型注釋
let func: MyFunction;
// 形參第一個(gè)參數(shù)為string類型, 第二個(gè)為number類型
func = function(p:string, s:number){
return s > 1 // 返回布爾類型
}
func('張三',10)
其實(shí)函數(shù)形參的類型已經(jīng)在接口中定義,因此函數(shù)形參可以不定義類型,會(huì)自動(dòng)使用接口中的類型來(lái)驗(yàn)證實(shí)參數(shù)據(jù)
// 定義檢測(cè)函數(shù)類型的接口
interface MyFunction{
// 調(diào)用簽名:形參第一個(gè)為string類型,第二個(gè)為number類型, 函數(shù)返回boolean類型
(params: string, subString:number):boolean
}
// 類型注釋
let func: MyFunction;
// 形參不用添加類型, 會(huì)自動(dòng)用接口中調(diào)用簽名參數(shù)的類型來(lái)匹配
func = function(p, s){
return s > 1 // 返回布爾類型
}
func('張三',10)
5.3 構(gòu)造簽名
JavaScript函數(shù)也可以通過(guò)new
操作符來(lái)調(diào)用, 也就是我們常說(shuō)的構(gòu)造函數(shù).用來(lái)創(chuàng)建新對(duì)象的函數(shù).
在TypeScript中,也可以通過(guò)在調(diào)用簽名前添加new
關(guān)鍵字來(lái)編寫(xiě)構(gòu)造函數(shù)簽名
例如:
// 接口定義對(duì)象類型
interface Person{
name:string;
age: number;
}
// 類型別名(構(gòu)造函數(shù)簽名)
type GreetFunction = {
new (n:string): Person // 構(gòu)造函數(shù)簽名(返回對(duì)象類型)
}
// 函數(shù)參數(shù)類型為構(gòu)造函數(shù)
function greeter(fn:GreetFunction){
// 參數(shù)是一個(gè)構(gòu)造函數(shù)
return new fn('張三')
}
// 類
class Student{
name:string
age: number
constructor(n:string){
this.name = n
this.age = 18
}
}
// 調(diào)用
const student = greeter(Student)
// const student: Person
console.log('student', student)
// {age: 18, name: "張三"}
6. 可選參數(shù)與默認(rèn)參數(shù)
6.1 實(shí)參必須與形參個(gè)數(shù)保持一致
TypeScriptl里的每個(gè)函數(shù)參數(shù)都是必須的傳遞的,TypeScript會(huì)檢查用戶是否為每個(gè)參數(shù)都傳入了值
// 聲明函數(shù)
function getUser(name:string, age:number) {
return {ame,age}
}
let user = getUser()
// 報(bào)錯(cuò): 應(yīng)有 2 個(gè)參數(shù),但獲得 0 個(gè)
let user2 = getUser('小明')
// 報(bào)錯(cuò): 應(yīng)有 2 個(gè)參數(shù)秩命,但獲得 1 個(gè)
let user3 = getUser('小明',18, '男')
// 報(bào)錯(cuò): 應(yīng)有 2 個(gè)參數(shù)尉共,但獲得 3 個(gè)
let user4 = getUser('小明',18) // 正常
示例中, 函數(shù)調(diào)用時(shí)傳遞給函數(shù)的實(shí)參個(gè)數(shù)必須與函數(shù)期望值(形參)的參數(shù)個(gè)數(shù)保持一致
但在JavaScript中函數(shù)的參數(shù)通常采用的都是可變數(shù)量的參數(shù), 也就是說(shuō)函數(shù)聲明時(shí)定義三個(gè)形參,但在函數(shù)調(diào)用時(shí), 傳遞的參數(shù)數(shù)量并不受限制.
6.2. 可選參數(shù)
而在TypeScript里我們可以通過(guò)?
將參數(shù)標(biāo)記為可選參數(shù).
例如:
// age標(biāo)記為可選參數(shù)
function getUser(name: string, age?: number) {
return {ame,age}
}
// 因?yàn)閍ge是可選參數(shù), 因此傳值不傳值都不會(huì)報(bào)錯(cuò)
let user1 = getUser('小明') // 正常
let user2 = getUser('小明',18) // 正常
let user3 = getUser('小明',18, '男')
// 報(bào)錯(cuò): 應(yīng)有 1-2 個(gè)參數(shù)褒傅,但獲得 3 個(gè)
說(shuō)明
- 可選參數(shù)必須跟在必須其他普通參數(shù)的后面, 因?yàn)楹瘮?shù)的參數(shù)是按順序傳遞的
- 可選參數(shù)會(huì)影響函數(shù)參數(shù)的個(gè)數(shù)(如,示例中函數(shù)應(yīng)該有1個(gè)參數(shù)或2個(gè)參數(shù))
如果將可選參數(shù)放在普通參數(shù)之前就會(huì)報(bào)錯(cuò)
function getUser(name?: string, age: number) {
return {ame,age}
}
// 報(bào)錯(cuò):必選參數(shù)不能位于可選參數(shù)后
6.3 默認(rèn)參數(shù)
在TypeScript里,我們也可以為參數(shù)提供一個(gè)默認(rèn)值. 當(dāng)用戶沒(méi)有傳遞實(shí)參或傳遞的實(shí)參值是undefined時(shí)袄友。 啟用默認(rèn)值.
// 參數(shù)帶有默認(rèn)值的函數(shù)
function getUser(name: string, age=18) {
return {ame,age}
}
// 啟用默認(rèn)值
let user1 = getUser('小明')
// 使用實(shí)參值
let user2 = getUser('小明',16)
// 顯示的傳遞undefined, 也會(huì)啟用默認(rèn)值, 跟JavaScript邏輯一樣
let user3 = getUser('小明',undefined)
注意: 帶默認(rèn)參數(shù)的形參放在所有形參后面, 如果放在前面需要啟用默認(rèn)值,必須手動(dòng)的傳遞undefined
一般帶有默認(rèn)值的參數(shù)不用添加類型注釋, 因?yàn)門(mén)ypeScript會(huì)根據(jù)默認(rèn)值推斷類型.
function getUser(name: string, age=18) {
return {ame,age}
}
getUser('小明', 'hello')
// 報(bào)錯(cuò): 類型“string”的參數(shù)不能賦給類型“number”的參數(shù)
7. 函數(shù)重載
在TypeScript中,我們可以通過(guò)編寫(xiě)重載簽名來(lái)指定一個(gè)可以以不同方式調(diào)用的函數(shù).
通常都是先編寫(xiě)同名函數(shù)簽名,然后在是函數(shù)體
例如: 編寫(xiě)一個(gè)函數(shù)生成時(shí)間, 參數(shù)可以接收一個(gè)時(shí)間戳參數(shù), 也可以接收年月日三個(gè)參數(shù)
// 函數(shù)重載簽名
// 只有一個(gè)參數(shù)(時(shí)間戳)的函數(shù)簽名
function makeDate(timestamp:number):Date
// 有三個(gè)參數(shù)(年月日)的函數(shù)簽名
function makeDate(y:number, m:number, d:number):Date
// 函數(shù)簽名必須有實(shí)現(xiàn)的函數(shù)體
// 要滿足兩個(gè)函數(shù)簽名, 函數(shù)體,第一個(gè)但是必須存在,有可能是兩個(gè)函數(shù)簽名中任意一個(gè)
// 而第二第三個(gè)參數(shù)在滿足第一個(gè)函數(shù)簽名時(shí)可能不存在, 定義為可選參數(shù)
function makeDate(yOrTimestamp:number, m?:number,d?:number ){
// 判斷第二第三個(gè)參數(shù)是否存在
if(m !== undefined && d !== undefined){
return new Date(yOrTimestamp,m,d)
}else{
return new Date(yOrTimestamp)
}
}
// 使用重載函數(shù)
// ok 參數(shù)可以是一個(gè)或三個(gè)
const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 5);
// 報(bào)錯(cuò)
const d3 = makeDate(1, 3);
// 報(bào)錯(cuò): 沒(méi)有需要 2 參數(shù)的重載殿托,但存在需要 1 或 3 參數(shù)的重載。
在這個(gè)例子中剧蚣,我們寫(xiě)了兩個(gè)重載:一個(gè)接受一個(gè)參數(shù)支竹,另一個(gè)接受三個(gè)參數(shù)。前兩個(gè)簽名稱為重載簽名鸠按。
然后編寫(xiě)了一個(gè)具體兼容簽名的函數(shù)實(shí)現(xiàn).
8. reset參數(shù)
8.1 剩余參數(shù)
TypeScript中,除了可以使用可選參數(shù)或重載來(lái)定義可以 接受各種固定參數(shù)計(jì)數(shù)的函數(shù)外,我們還可以使用剩余參數(shù)定義接受無(wú)限數(shù)量的函數(shù)
reset參數(shù)出現(xiàn)在所有其他參數(shù)之后, 并使用...
語(yǔ)法
function getUser(name: string, age:number, ...arg: string[]) {
// ...剩余運(yùn)算符將所有形參沒(méi)接受完的實(shí)參組成數(shù)組
return {name,age,hobby: arg}
}
let user = getUser('小明',18, '游泳','游戲','籃球')
console.log('user', user)
剩余參數(shù)會(huì)將所有沒(méi)有變量接受的實(shí)參放入數(shù)組中
在Type中, 剩余參數(shù)的類型注釋是隱式的any[]
類型 而不是any
,普通參數(shù)的隱式類型為any
.
在這給剩余參數(shù)添加類型注釋時(shí)必須是Array<T>
,T[]
或元組類型.否則會(huì)報(bào)錯(cuò)
function getUser(name: string, ...arg: string) {
// 報(bào)錯(cuò): rest 參數(shù)必須是數(shù)組類型礼搁。
}
8.2 擴(kuò)展運(yùn)算符
相反, 我們也可以使用擴(kuò)展語(yǔ)法從數(shù)組中獲取可變數(shù)據(jù)參數(shù), 例如,數(shù)組的 push方法可以接受任意數(shù)量的參數(shù)
let arr = [1,2,3]
let arr2 = [4,5,6]
// 擴(kuò)展arr2
arr.push(...arr2)
請(qǐng)注意,通常,TypeScript并不假定數(shù)組 是不可變的, 也就是數(shù)組內(nèi)的數(shù)量是不確定, 因此就可能會(huì)導(dǎo)致令人驚訝的行為
例如:
const args = [8, 5];
const angle = Math.atan2(...args);
// 報(bào)錯(cuò):擴(kuò)展參數(shù)必須具有元組類型或傳遞給 rest 參數(shù)。
示例報(bào)錯(cuò)了, 此時(shí)將鼠標(biāo)移入atan2
方法上就會(huì)發(fā)現(xiàn)
(method) Math.atan2(y: number, x: number): number
atan2
接受的是兩個(gè)數(shù)字類型的參數(shù), 但是args
的參數(shù)是不定的, 擴(kuò)展之后并不能匹配,因此這就是TypeScript報(bào)錯(cuò)的原因
同時(shí)TypeScript報(bào)錯(cuò)告訴我們的是,函數(shù)或方法中如果像使用擴(kuò)展語(yǔ)法, 前提這個(gè)方法必須具有剩余參數(shù)或傳遞的參數(shù)為元組類型(因?yàn)樵M類型數(shù)量固定)
那我們可以看下push
方法的類型是什么? 為什么push
可以擴(kuò)展, 鼠標(biāo)移入push
(method) Array<number>.push(...items: number[]): number
我們會(huì)發(fā)現(xiàn)push
類型參數(shù)是剩余運(yùn)算符, 也就是說(shuō)不管你傳遞多少參數(shù), 會(huì)被剩余參數(shù)打包到數(shù)組中
<br.>
其實(shí)我們自己知道我們 給atan2
方法傳遞的數(shù)組只有兩項(xiàng)目,擴(kuò)展后是完全符合參數(shù)調(diào)節(jié), 但TypeScript識(shí)別數(shù)組是項(xiàng),
因此解決這個(gè)問(wèn)題的最佳方案就是使用使用斷言為const
, 將數(shù)組類型轉(zhuǎn)為元組類型.
const args = [8, 5];
// const args: number[]
const args2 = [8, 5] as const;
// const args2: readonly [8, 5]
const angle = Math.atan2(...args2);
原有的args
是一個(gè)number[]
類型, 通過(guò)as const
斷言后,args
變成了只讀的元組類型
9. 參數(shù)解構(gòu)
也可以使用參數(shù)解構(gòu) 來(lái)方便地將作為參數(shù)提供的對(duì)象解壓到函數(shù)體中的一個(gè)或多個(gè)局部變量中.
JavaScript中解構(gòu)是這樣的
function sum({a,b,c}){
return a + b + c
}
sum({a:10, b:20, c: 30})
此時(shí)加上TypeScript中對(duì)象類型注釋就變成這樣
// 解構(gòu)參數(shù): 對(duì)象類型
function sum({a,b,c}: {a:number,b:number,c:number}){
return a + b + c
}
sum({a:10, b:20, c: 30})
還可以將對(duì)象類型通過(guò)類型別名或接口抽離
// 類型別名
type ABC = {a:number,b:number,c:number}
// 解構(gòu)參數(shù): 對(duì)象類型
function sum({a,b,c}:ABC ){
return a + b + c
}
sum({a:10, b:20, c: 30})
10. 通用函數(shù)
10.1 泛型函數(shù)
通常會(huì)編寫(xiě)一個(gè)函數(shù), 其中輸出的類型和輸入的類型相關(guān),或者兩個(gè)輸入的類型類型以某種方式相關(guān).
例如:返回 數(shù)組第一個(gè)元素函數(shù)
function firstElement(arr:any[]){
return arr[0]
}
// 類型: function firstElement(arr: any[]): any
這個(gè)函數(shù),接受一個(gè)any[]
類型的數(shù)組, 并返回any
類型,我們希望的是如果能返回函數(shù)具體的類型會(huì)更好
在TypeScript中, 當(dāng)我們想要描述兩個(gè)值之間的對(duì)應(yīng)關(guān)系, 會(huì)使用泛型, 通過(guò)在函數(shù)簽名中聲明一個(gè)類型參數(shù)來(lái)做到這一點(diǎn)
這個(gè)類型參數(shù)當(dāng)成函數(shù)參數(shù)來(lái)理解, 只不過(guò)函數(shù)參數(shù)是用來(lái)接受值的, 而類型參數(shù)是用來(lái)接受類型的,
就像類型別名可以當(dāng)成變量理解一樣
例如:
// 變量num 接受了值
const num = 10
// 類型別名接受一個(gè)類型
type Hello = string
而類型參數(shù)也是如此
例如:
// 普通函數(shù)
function fn(num){
// num 參數(shù)接受一個(gè)值,
// 在函數(shù)體內(nèi)通過(guò)num來(lái)一直調(diào)用這個(gè)值
console.log(num)
}
// 泛型
function firstElement<Type>(arr:Type[]):Type | undefined{
// <>中的Type就是類型參數(shù), Type接受一個(gè)類型
// 函數(shù)體內(nèi), 包括參數(shù)類型, 返回類型都可以通過(guò)類型參數(shù)使用同一個(gè)類型
return arr[0]
}
通過(guò)向這個(gè)函數(shù)添加一個(gè)類型參數(shù)Type
并在兩個(gè)地方使用它目尖,我們?cè)诤瘮?shù)的輸入(數(shù)組)和輸出(返回值)之間創(chuàng)建了一個(gè)鏈接÷猓現(xiàn)在當(dāng)我們調(diào)用它時(shí),會(huì)出現(xiàn)一個(gè)更具體的類型:
/*
在調(diào)用函數(shù)的使用, 我們傳入了類型string
那么firsetElement 函數(shù)內(nèi)Type類型參數(shù)就是一個(gè)string類型
函數(shù)會(huì)接受一個(gè)string[]數(shù)組, 并返回 string | undefined 聯(lián)合類型
*/
const s = firstElement<string>(['hello', 'world'])
// const s: string
// 同樣可以傳入其他類型
const n = firstElement<number>([10, 20])
// const n: number
注意, 你如果指定了Type
是某種類型, 例如string
類型, 那么函數(shù)firstElement
參數(shù)就通過(guò)Type
關(guān)聯(lián)這, 需要接受一個(gè)string[]
, 如果你傳入的參數(shù)不對(duì)將會(huì)報(bào)錯(cuò)
/*
指定Type 是string類型, 傳遞的number類型就不匹配
*/
const s = firstElement<string>(['hello', 'world', 20])
// const s: string
// 20報(bào)錯(cuò): 不能將類型“number”分配給類型“string”
10.2 泛型的推理
有事我們不必明確指定Type
的類型, 類型可以有TypeScript推斷得到
例如:上例中的調(diào)用我們可以去掉指定類型
/*
調(diào)用函數(shù)時(shí)沒(méi)有在<>中明確指定Type類型
但是我們將[10,20] 傳遞給函數(shù)時(shí), 函數(shù)推斷出為number[]類型
而number[] 有時(shí)需要去匹配Type[] 類型的, Type 并沒(méi)有指定
因此TypeScript推斷出 Type類型參數(shù)為 number類型
*/
const n = firstElement([10, 20])
// const n: number
也可以使用多個(gè)類型參數(shù)
例如:
/*
函數(shù)map中有兩個(gè)類型參數(shù) Input Output
函數(shù)普通參數(shù)接受兩個(gè)參數(shù)
1. arr為Input[]
2. func是一個(gè)函數(shù), 此函數(shù)接受一個(gè)Input類型(arr的項(xiàng)),返回Output類型
函數(shù)整體返回 Output[]
*/
function map<Input, Output>(arr:Input[], func: (arg:Input) => Output): Output[]{
return arr.map(func)
}
/*
調(diào)用函數(shù)時(shí)沒(méi)有指定Input,Output類型
通過(guò)第一個(gè)參數(shù)['1','2','3'] Input 是string 類型
第二個(gè)參數(shù)是函數(shù)
1.函數(shù)參數(shù)n的類型就是string類型
2.函數(shù)返回值 parseInt(n)就是一個(gè) number類型, 因此推斷 Output是一個(gè)number類型
函數(shù)map整體返回Output[] 也就是 number[]類型
*/
const result = map(['1','2','3'], (n) => parseInt(n))
// const result: number[]
10.3 約束
編寫(xiě)通用函數(shù),處理任何類型的值, 但有時(shí)我們可能需要使用約束來(lái)限制類型參數(shù)可以接受類型的種類
例如:編寫(xiě)一個(gè)函數(shù)比較兩個(gè)參數(shù)length
屬性的長(zhǎng)度
function longest<Type>(a:Type,b:Type){
if(a.length >= b.length){
return a
}else{
return
}
}
// 報(bào)錯(cuò):類型“Type”上不存在屬性“l(fā)ength”瑟曲。
如果這么編寫(xiě)通用函數(shù), 那么就會(huì)報(bào)錯(cuò), 告訴我們類型Type
上不存在length
屬性
Type
只是類型參數(shù), 可以是任何類型,如果調(diào)用函數(shù)傳入number
類型, number
是沒(méi)有length
屬性的
因此我們可以通過(guò)約束來(lái)限制Type
只能接受大有length
屬性的參數(shù)
通過(guò)編寫(xiě)一個(gè)字句將類型參數(shù)限制為具有length
屬性對(duì)象類型, 然后通過(guò)extends
來(lái)擴(kuò)展Type
類型參數(shù)
例如:
function longest<Type extends {length:number}>(a:Type,b:Type){
if(a.length >= b.length){
return a
}else{
return
}
}
// 此時(shí)通用函數(shù)就不報(bào)錯(cuò), 但是調(diào)用函數(shù)傳入的參數(shù)必須滿足具有l(wèi)ength屬性
// Type 為 'number[]'
const arr = longest([1, 2], [1, 2, 3]);
// Type 為 'alice' | 'bob'
const str = longest("alice", "bob");
// 報(bào)錯(cuò)
const notOK = longest(10, 100);
// 類型“number”的參數(shù)不能賦給類型“{ length: number; }”的參數(shù)乙埃。
10.4 指定類型參數(shù)
TypeScript 通潮诨可以在泛型調(diào)用中推斷出預(yù)期的類型參數(shù),但并非總是如此
例如:
function combine<Type>(arr1: Type[], arr2: Type[]): Type[] {
return arr1.concat(arr2);
}
// 如果此時(shí)使用不匹配的數(shù)組調(diào)用函數(shù)就會(huì)報(bào)錯(cuò)
const arr = combine([1, 2, 3], ["hello"]);
//不能將類型“string”分配給類型“number
示例中, TypeScript通過(guò)第一個(gè)參數(shù)推斷出Type的類型為number
類型, 緊接著你就講一個(gè)string[]
賦值給一個(gè)類型為number[]
的參數(shù), 就報(bào)錯(cuò)了
如果你打算真的這樣傳遞參數(shù), 那么你可以手動(dòng)的指定Type 為string | number
聯(lián)合類型
const arr = combine<string | number>([1, 2, 3], ["hello"]);