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ù)類型和返回值類型。
- 參數(shù)名不一定要相同,只要參數(shù)類型匹配,那么就認(rèn)為它是有效的函數(shù)類型。
const sum: (num1: number, num2: number) => number = (x, y) => x + y
- 返回值類型是函數(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ù)不同之處:
- 沒有傳值時(shí)默認(rèn)參數(shù)是取默認(rèn)值,而可選參數(shù)的值是 undefined
- 帶默認(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 指定的更明確的類型。