第五節(jié):TypeScript函數(shù)

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ō)明:

  1. (s:string) =void 函數(shù)類型表示接受一個(gè)string類型參數(shù), 無(wú)返回值
  2. (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ō)明

  1. 可選參數(shù)必須跟在必須其他普通參數(shù)的后面, 因?yàn)楹瘮?shù)的參數(shù)是按順序傳遞的
  2. 可選參數(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"]);
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子巍耗,更是在濱河造成了極大的恐慌抓歼,老刑警劉巖嚷闭,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勤众,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡琉挖,警方通過(guò)查閱死者的電腦和手機(jī)启泣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)示辈,“玉大人寥茫,你說(shuō)我怎么就攤上這事》椋” “怎么了纱耻?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)险耀。 經(jīng)常有香客問(wèn)我弄喘,道長(zhǎng),這世上最難降的妖魔是什么甩牺? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任蘑志,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘急但。我一直安慰自己澎媒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布波桩。 她就那樣靜靜地躺著戒努,像睡著了一般。 火紅的嫁衣襯著肌膚如雪镐躲。 梳的紋絲不亂的頭發(fā)上储玫,一...
    開(kāi)封第一講書(shū)人閱讀 52,441評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音萤皂,去河邊找鬼撒穷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛裆熙,可吹牛的內(nèi)容都是我干的桥滨。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼弛车,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蒲每?” 一聲冷哼從身側(cè)響起纷跛,我...
    開(kāi)封第一講書(shū)人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎邀杏,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體唤崭,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡脖律,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年谢肾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芦疏。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡微姊,死狀恐怖酸茴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情兢交,我是刑警寧澤薪捍,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布酪穿,位于F島的核電站,受9級(jí)特大地震影響纺座,放射性物質(zhì)發(fā)生泄漏溉潭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一馋贤、第九天 我趴在偏房一處隱蔽的房頂上張望畏陕。 院中可真熱鬧,春花似錦犹芹、人聲如沸鞠绰。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)翁巍。三九已至,卻和暖如春肝断,著一層夾襖步出監(jiān)牢的瞬間驰凛,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工箫荡, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留渔隶,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓利术,卻偏偏與公主長(zhǎng)得像低矮,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子轮蜕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容