TypeScript 定義函數(shù)的四種方式
第一種方式可以直接調(diào)用寄狼,后三種需要先實現(xiàn)定義的函數(shù)再調(diào)用履婉。
第一種 函數(shù)聲明式:
function sum(x: number, y: number): number {
return x + y
}
// 調(diào)用時形參和實參一一對應(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)
第三種 接口實現(xiàn):
interface ISum {
(x: number, y: number): number
}
// 跟變量聲明是等價的: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 編譯器會自動識別出類型, 這叫做“按上下文歸類”窒舟,是類型推論的一種。
const getSum: (x: number, y: number) => number = (x, y) => x + y
可選參數(shù)
JavaScript 里诵盼,每個參數(shù)都是可選的惠豺。沒傳參的時候,值就是 undefined风宁。但在 TypeScript 中函數(shù)參數(shù)默認(rèn)都是必傳的洁墙,必傳的意思并不是不能傳遞 null 和 undefined 作為實參,而是編譯器會檢查是否為每個參數(shù)傳入了值戒财。簡而言之热监,編譯器會檢查傳入實參的個數(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)識符 實現(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ù)提供一個默認(rèn)值列吼,當(dāng)參數(shù)是可選的且沒有傳值或傳遞的值是 undefined 時,則會使用參數(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ù)不同之處:
- 沒有傳值時默認(rèn)參數(shù)是取默認(rèn)值寞钥,而可選參數(shù)的值是 undefined
- 帶默認(rèn)值的參數(shù)不需要放在必選參數(shù)的后面。如果帶默認(rèn)值的參數(shù)出現(xiàn)在必選參數(shù)前面陌选,則調(diào)用時必須明確的傳入
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
模式開啟時報錯(this' implicitly has type 'any' because it does not have a type annotation)您炉,未開啟時指向 window。
可以將返回函數(shù)設(shè)置成箭頭函數(shù)解決該問題役电。
const person = {
name: 'Mike',
getName() {
return () => {
console.log(this.name)
}
}
}
const getName = person.getName()
getName() // 'MIke'
但上面的代碼還是會存在一些問題赚爵。因為即使能夠保證箭頭函數(shù)里面的 this 與外層函數(shù)的 this 保持一致, 但是外層函數(shù)的 this 不一定就是 person 這個對象宴霸。函數(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ù)重載允許一個函數(shù)通過不同數(shù)量或類型的參數(shù),返回不同類型的值氓扛。
比如:實現(xiàn)一個函數(shù) reverse枯芬,輸入數(shù)字的時候,輸出反轉(zhuǎn)的數(shù)字采郎,輸入字符串的時候千所,輸出反轉(zhuǎn)的字符串。
通過聯(lián)合類型實現(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á)不同的輸入類型所對應(yīng)的輸出類型蒜埋。 可以通過函數(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'
以上前兩個函數(shù)是函數(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ù)的一個重要的應(yīng)用場景是實現(xiàn)自定義類型守衛(wèi)。
類型謂詞只能用來定義自定義類型守衛(wèi),實際上是告訴引擎谱仪,當(dāng)守衛(wèi)條件成立的情況下(返回 true)玻熙,將被守衛(wèi)的類型縮小到 is 指定的更明確的類型。