- TypeScript函數類型
- TypeScript函數的參數
- TypeScript函數的this與箭頭函數
- TypeScript函數重載
一、TypeScript函數類型
在上一篇博客中已經對聲明TypeScript類型變量已經做了初步的解析,這里先回顧以下:
1 //聲明函數 2 function add1(x:number,y:number):number{ 3 return x+y; 4 } 5 6 let add2 = function(x:number,y:number):number{ 7 return x + y; 8 } 9 10 //定義函數類型 11 type myAdd = (baseValue: number, increment: number) => number; 12 //根據函數類型聲明函數變量 13 let myAdd1:myAdd = function(x:number,y:number) : number{ 14 return x + y; 15 } 16 let myAdd2:myAdd = function(x:number,y:number) : number{ 17 return x - y; 18 } 19 20 let hintStamp(str:string):void{ 21 console.log(str); 22 }
關於函數類型有一點需要注意,需要使用type關鍵字並使用等號"="賦值類型;如果使用let聲明並使用冒號冒“:”表示的是聲明一個函數變量,並且這個函數變量有指定的類型,這個變量不能作為函數類型,只能給它自己賦值指定類型的函數。
二、TypeScript函數的參數
2.1 根據函數類型聲明的函數變量,函數的實際參數名稱可以不與函數類型的參數名稱一致,這一點與對象類型Object的字段有點區別;
1 type myAdd = (baseValue: number, increment: number) => number; 2 let myAdd1:myAdd = function(a:number,b:number) : number{ 3 return a + b; 4 } 5 let myAdd2:myAdd = function(x:number,y:number) : number{ 6 return x - y; 7 }
2.2 可選參數與默認參數:
這部非內容在官方文檔中有詳細的說明,這里簡要的解析以下,如果有不明白的建議查看官方文檔。
可選參數意思就是函數的參數在實際調用函數時,可以不需要全部對應傳入,但是在定義函數類型時,必須設定默認參數。
1 //示例:在定義函數類型時,給參數b設定了默認參數:'TypeScript' 2 function foo (a: string,b = 'TypeScript'): void{ 3 console.log(a + ':' + b); 4 } 5 foo('hello');//hello:TypeScript
設置了默認參數,就不需要設置參數的類型了,TypeScript的IDE會主動為我們推斷出參數的類型,也就是默認值的類型,如果給可選參數傳值就必須對應默認參數的類型。
除了設置既定的默認參數以外,還可以使用(參數名+?)的方式設置非特定參數,但是設置默認非特定參數需要設置默認參數的類型(參數名?:類型)。這種不設定特定值的可選參數,調用函數時不給這個可選參數傳值的話就會默認為undefined。
1 //這是官方文檔中的一個示例: 2 function buildName(firstName: string, lastName?: string) { 3 if (lastName){ 4 5 return firstName + " " + lastName; 6 }else{ 7 return firstName; 8 } 9 } 10 11 let result1 = buildName("Bob"); // works correctly now 12 console.log(result1); 13 let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters 14 let result3 = buildName("Bob", "Adams"); // ah, just right
2.3 剩余參數
關於剩余參數其本質就是ES6的收集(res)語法,使用"..."將多余的參數收集,但是TypeScript轉換到ES5的js代碼時還是需要使用arguments來實現,畢竟ES5版本中還沒有收集(res)語法。詳細可以了解ES6入門一:塊級作用域(let&const)、spread展開、rest收集
1 function buildName(firstName: string, ...restOfName: string[]) { 2 return firstName + " " + restOfName.join(" "); 3 } 4 5 let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
但是需要注意,收集語法產生的數據必然是數組,所以必須使用數組類型。
三、TypeScript函數的this與箭頭函數
關於this指向在JavaScript中一直是比較容易被它擊暈的一個知識點,不要擔心,我這里有一篇博客包治百病:JavaScript中的this指向規則
當然ES6的相關語法產生的this指向規則變化這里也有:ES6入門五:箭頭函數、函數與ES6新語法
當然好不了嚴格模式的this指向問題:JavaScript嚴格模式
TypeScript並不改變JavaScript的this指向規則,而是在IDE中提供了更友好的警告提示,這個功能需要在tsconfig.json中通過"noImplicitThis": true開啟。我們知道在JavaScript中this指向規則是比較復雜的,而TypeScript也根據這些規則做出了相應的調整,這里關於TypeScript函數的this指向其實就是了解"noImplicitThis"給我們的提示規則是什么?
3.1 TypeScript對象返回函數中的this會警告提示:
1 let obj = { 2 strs : ["aaa","bbb","ccc","ddd"], 3 fun:function(){ 4 return function() { //這里會提示this出錯,因為在嚴格模式下this可能為undefined,在TypeScript中IDE識別this類型為any,所以被認為可能出現undefined情況 5 let index = Math.floor(Math.random() * this.strs.length); 6 return this.strs[index]; 7 } 8 } 9 } 10 var fun = obj.fun(); 11 console.log(fun());
這個問題可以使用箭頭函數來解決:
1 let obj = { 2 strs : ["aaa","bbb","ccc","ddd"], 3 fun:function(){ 4 return () => { //通過箭頭函數解決 5 let index = Math.floor(Math.random() * this.strs.length); 6 return this.strs[index]; 7 } 8 } 9 } 10 var fun = obj.fun(); 11 console.log(fun());
3.2 this作為函數參數,不會出現錯誤提示,這一點需要注意,因為在原生JavaScritp種這種寫法會直接出現語法錯誤。例如示例中這樣寫:
1 function f(this:void){//這里你可以給形參任意TypeScript類型都不會報錯 2 //很遺憾這里並不會報錯 3 }
這里有幾方面的原因,嚴格模式下函數只執行內部this都是指向undefined,如果函數被對象調用this必然也就指向調用函數的對象。更關鍵的是被寫在形參內的this根本就不會被TypeScript編譯到原生js代碼中,這里不報錯其實是TypeScript將這個低級的錯誤幫你自動處理了,所以就靜默了這個錯誤提示。
關於this作為參數在TypeScript中還有另一個功能,就是幫助函數維持this指向,例如官方這個示例:
1 interface Card { 2 suit: string; 3 card: number; 4 } 5 interface Deck { 6 suits: string[]; 7 cards: number[]; 8 createCardPicker(this: Deck): () => Card; 9 } 10 let deck: Deck = { 11 suits: ["hearts", "spades", "clubs", "diamonds"], 12 cards: Array(52), 13 // NOTE: The function now explicitly specifies that its callee must be of type Deck 14 createCardPicker: function(this: Deck) { 15 return () => { 16 let pickedCard = Math.floor(Math.random() * 52); 17 let pickedSuit = Math.floor(pickedCard / 13); 18 19 return {suit: this.suits[pickedSuit], card: pickedCard % 13}; 20 } 21 } 22 } 23 24 let cardPicker = deck.createCardPicker(); 25 let pickedCard = cardPicker(); 26 27 alert("card: " + pickedCard.card + " of " + pickedCard.suit);
上面這段代碼中14行的createCardPicker: function(this: Deck){}被編譯成了這樣:
1 createCardPicker: function () { 2 var _this = this; 3 }
上面這種代碼是我們常用維持this指向的手段,在定義接口時常見的代碼,在TypeScript中它使用this作為參數的方式來實現,這樣的處理方式可以非常明顯的看到this指向了對象自身,並且如果這個方法被賦值給其他對象變量時同樣會靜默這個this參數的錯誤,而其他對象在TypeScript嚴格的變量類型語法中也可能獲取這個方法作為自身參數。
3.3 this在回調函數中如何維持this指向?下面是將官方文檔代碼補全的示例:
1 interface UIElement { 2 addClickListener(onclick: (this: void, e: Event) => void): void; 3 } 4 5 class Handler { 6 info: string; 7 constructor(infoStr:string){ 8 this.info = infoStr; 9 } 10 onClickGood = (e: Event) => {//通過箭頭函數來保持函數內部this不變 11 // can't use this here because it's of type void! 12 console.log(this);//指向Handler的實例對象 13 console.log(this.info);//info 14 console.log('clicked!'); 15 } 16 } 17 let h = new Handler('info'); 18 class uiElementClass implements UIElement{ 19 addClickListener(onclick: (this: void, e: Event) => void): void{ 20 onclick(new Event('event')); 21 } 22 } 23 let uiElement:uiElementClass = new uiElementClass(); 24 uiElement.addClickListener(h.onClickGood);
四、TypeScript函數重載
關於TypeScript函數我想了好久就是不知道怎么解析這個東西,照着示例套吧,我也只能復制官方代碼放這里了。
1 let suits = ["hearts", "spades", "clubs", "diamonds"]; 2 3 function pickCard(x: {suit: string; card: number; }[]): number; 4 function pickCard(x: number): {suit: string; card: number; }; 5 function pickCard(x:any): any { 6 // Check to see if we're working with an object/array 7 // if so, they gave us the deck and we'll pick the card 8 if (typeof x == "object") { 9 let pickedCard = Math.floor(Math.random() * x.length); 10 return pickedCard; 11 } 12 // Otherwise just let them pick the card 13 else if (typeof x == "number") { 14 let pickedSuit = Math.floor(x / 13); 15 return { suit: suits[pickedSuit], card: x % 13 }; 16 } 17 } 18 19 let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }]; 20 let pickedCard1 = myDeck[pickCard(myDeck)]; 21 alert("card: " + pickedCard1.card + " of " + pickedCard1.suit); 22 23 let pickedCard2 = pickCard(15); 24 alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);
