TypeScript入門三:TypeScript函數類型


  • 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);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM