TypeScript(五)函數(shù)

TypeScript 定義函數(shù)的四種方式

第一種方式可以直接調(diào)用,后三種需要先實(shí)現(xiàn)定義的函數(shù)再調(diào)用。

第一種 函數(shù)聲明式:

function sum(x: number, y: number): number {
  return x + y
}
// 調(diào)用時(shí)形參和實(shí)參一一對(duì)應(yīng)
sum(1, 2)

第二種 函數(shù)表達(dá)式:

let sum: (x: number, y: number) => number = (a, b) => a + b
// 或
let sumX = (a: number, b: number): number => a + b
sum(2, 2)
sumX(1, 4)

第三種 接口實(shí)現(xiàn):

interface ISum {
  (x: number, y: number): number
}
// 跟變量聲明是等價(jià)的:let ISum: (a: number, b: number) => number
let sum: ISum = (a, b) => a + b
sum(4, 2)

第四種 類型別名:(推薦方式)

type ISum = (x: number, y: number) => number
// 應(yīng)用如下:
let sum: ISum = (a, b) => a + b
sum(3, 2)

函數(shù)類型

函數(shù)類型包含兩部分:參數(shù)類型和返回值類型。

  1. 參數(shù)名不一定要相同,只要參數(shù)類型匹配,那么就認(rèn)為它是有效的函數(shù)類型。
const sum: (num1: number, num2: number) => number = (x, y) => x + y
  1. 返回值類型是函數(shù)類型的必要部分,如果函數(shù)沒有返回任何值,則返回值類型為 void。
const sum: (num1: number, num2: number) => void = (x, y) => {
  console.log(x + y)
}

類型推斷

如果在賦值語句的一邊指定了類型但另一邊沒有類型的話,TypeScript 編譯器會(huì)自動(dòng)識(shí)別出類型, 這叫做“按上下文歸類”,是類型推論的一種。

const getSum: (x: number, y: number) => number = (x, y) => x + y

可選參數(shù)

JavaScript 里,每個(gè)參數(shù)都是可選的。沒傳參的時(shí)候,值就是 undefined。但在 TypeScript 中函數(shù)參數(shù)默認(rèn)都是必傳的,必傳的意思并不是不能傳遞 null 和 undefined 作為實(shí)參,而是編譯器會(huì)檢查是否為每個(gè)參數(shù)傳入了值。簡(jiǎn)而言之,編譯器會(huì)檢查傳入實(shí)參的個(gè)數(shù)是否和形參相同。

function bar(name: string, age: number): string {
  return `${name} age is ${age}`
}

bar('jack', 12) // ok
bar('nike') // Expected 2 arguments, but got 1.
bar('rose', 12, 'shanghai') // Expected 2 arguments, but got 3.

TypeScript 的可選參數(shù)需要在參數(shù)名后使用 ? 標(biāo)識(shí)符 實(shí)現(xiàn)可選參數(shù)的功能。 比如上例希望 age 是可選的:

function bar(name: string, age?: number): string {
  if (age) return `${name} age is ${age}`
  return `the name is ${name}`
}

bar('jack', 12) // ok
bar('nike') // ok
bar('rose', 12, 'shanghai') // Expected 1-2 arguments, but got 3.

注意: 可選參數(shù)必須在必選參數(shù)后面,且后面不能再有必選參數(shù)

參數(shù)默認(rèn)值

可以通過為參數(shù)提供一個(gè)默認(rèn)值,當(dāng)參數(shù)是可選的且沒有傳值或傳遞的值是 undefined 時(shí),則會(huì)使用參數(shù)的默認(rèn)值。

function fullName(firstName: string, lastName: string = 'Smith') {
  return `${firstName}  ${lastName}`
}

fullName('Bob') // Bob  Smith
fullName('Bob', undefined) // Bob  Smith
fullName('Bob', 'Adams', 'Sr.') // Expected 1-2 arguments, but got 3.
fullName('Bob', 'Adams') // Bob  Adams

參數(shù)默認(rèn)值與可選參數(shù)不同之處:

  1. 沒有傳值時(shí)默認(rèn)參數(shù)是取默認(rèn)值,而可選參數(shù)的值是 undefined
  2. 帶默認(rèn)值的參數(shù)不需要放在必選參數(shù)的后面。如果帶默認(rèn)值的參數(shù)出現(xiàn)在必選參數(shù)前面,則調(diào)用時(shí)必須明確的傳入 undefined 值來取得默認(rèn)值

剩余參數(shù)

TypeScript 的剩余參數(shù)和 ES6 的剩余參數(shù)一樣。

interface ITotal {
  (pre: number, cur: number): number
}

function sum(num1: number, ...rest: number[]): number {
  const handle: ITotal = (pre, cur) => pre + cur

  return rest.reduce(handle, num1)
}

sum(1, 2, 3, 4, 5, 6, 7) // 28

this

this 與箭頭函數(shù)

TypeScript 在 noImplicitThis 模式下,不允許 this 上下文隱式定義。

const person = {
  name: 'Mike',
  getName() {
    return function () {
      console.log(this.name)
    }
  }
}
const getName = person.getName()
getName()

上例函數(shù)中的 this 在 noImplicitThis 模式開啟時(shí)報(bào)錯(cuò)(this' implicitly has type 'any' because it does not have a type annotation),未開啟時(shí)指向 window。

可以將返回函數(shù)設(shè)置成箭頭函數(shù)解決該問題。

const person = {
  name: 'Mike',
  getName() {
    return () => {
      console.log(this.name)
    }
  }
}
const getName = person.getName()
getName() // 'MIke'

但上面的代碼還是會(huì)存在一些問題。因?yàn)榧词鼓軌虮WC箭頭函數(shù)里面的 this 與外層函數(shù)的 this 保持一致, 但是外層函數(shù)的 this 不一定就是 person 這個(gè)對(duì)象。函數(shù)中的 this 依舊是 any 類型。

this 參數(shù)

在 JavaScript 中,this 不能用做變量或參數(shù)名,所以 TypeScript 使用語法空間來讓你在函數(shù)體中聲明 this 的類型。this 類型聲明必須放在參數(shù)的首位:

interface IPerson {
  name: string
  getInfo(this: IPerson, age: number): string
}

const info: IPerson = {
  name: 'jack',
  getInfo(age) {
    return this.name + age
  }
}

info.getInfo(23) // ?
const info1: IPerson = {
  name: 'rose',
  getInfo(age) {
    return this.name + age
  }
}
info1.getInfo(34) // ?

const obj = {
  name: 'mike'
}

info.getInfo.call(obj, 45) // ?
// obj 不是IPerson的類型

重載

函數(shù)重載允許一個(gè)函數(shù)通過不同數(shù)量或類型的參數(shù),返回不同類型的值。

比如:實(shí)現(xiàn)一個(gè)函數(shù) reverse,輸入數(shù)字的時(shí)候,輸出反轉(zhuǎn)的數(shù)字,輸入字符串的時(shí)候,輸出反轉(zhuǎn)的字符串。

通過聯(lián)合類型實(shí)現(xiàn):

function reverse(val: number | string): number | string {
  if (typeof val === 'number') {
    return Number(val.toString().split('').reverse().join(''))
  } else if (typeof val === 'string') {
    return val.split('').reverse().join('')
  }
}

聯(lián)合類型的缺陷是不能精確表達(dá)不同的輸入類型所對(duì)應(yīng)的輸出類型。 可以通過函數(shù)重載定義多個(gè)函數(shù)類型。

函數(shù)重載實(shí)現(xiàn):

function reverse(num: number): number
function reverse(str: string): string
function reverse(val: any): number | string | boolean {
  if (typeof val === 'number') {
    return Number(val.toString().split('').reverse().join(''))
  } else if (typeof val === 'string') {
    return val.split('').reverse().join('')
  }
  return false
}

console.log(reverse(123456)) // 654321
console.log(reverse('sina')) // 'anis'

以上前兩個(gè)函數(shù)是函數(shù)重載列表,第三個(gè)是函數(shù)實(shí)體。

注意:重載只能通過 function 聲明。

類型謂詞(is)

在 TypeScript 中,函數(shù)還支持另外一種特殊的類型描述,如下示例 :

function isString(s): s is string { // 類型謂詞
  return typeof s === 'string';
}
function isNumber(n: number) {
  return typeof n === 'number';
}
function operator(x: unknown) {
  if(isString(x)) { // ok x 類型縮小為 string
  }
  if (isNumber(x)) { // ts(2345) unknown 不能賦值給 number
  }
}

在上述代碼中,在添加返回值類型的地方,通過“參數(shù)名 + is + 類型”的格式明確表明了參數(shù)的類型,進(jìn)而引起類型縮小,所以類型謂詞函數(shù)的一個(gè)重要的應(yīng)用場(chǎng)景是實(shí)現(xiàn)自定義類型守衛(wèi)。

類型謂詞只能用來定義自定義類型守衛(wèi),實(shí)際上是告訴引擎,當(dāng)守衛(wèi)條件成立的情況下(返回 true),將被守衛(wèi)的類型縮小到 is 指定的更明確的類型。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容