typescript-函數的類型

https://www.cnblogs.com/wzy1569178479/p/7364340.html

1.函數聲明

1 function student(x:string,y:number):string{
2     return `我是${x},今年${y}歲`;
3 }
4 
5 console.log(student("wzy",22));  // 我是wzy,今年22歲

形式和JavaScript中的函數聲明一樣,不一樣的是什麼呢?

(1)指定了參數的類型(因爲有類型檢查,所以要遵循),隨後指定了返回值的類型,

這個時候返回值類型可以省略,因爲typescript會根據返回語句自動推斷出返回值的類型。

(2)參數不可多不可少,只能剛剛好,且和順序有關。

1 function student(x:string,y:number){
2     console.log(`我是${x},今年${y}歲`);
3 }
4 
5 student(22,"wzy"); 
6 //  Argument of type '22' is not assignable to parameter of type 'string'.

2.函數表達式

有些話總是需要照抄下來的,比如下面這兩句話

如果要我們現在寫一個對函數表達式(Function Expression)的定義,可能會寫成這樣:

1 let student = function(x:string,y:number):string{
2     return `我是${x},今年${y}歲`;
3 }
4 
5 console.log(student("wzy",22));  // 我是wzy,今年22歲

這是可以通過編譯的,不過事實上,上面的代碼只對等號右側的匿名函數進行了類型定義,而等號左邊的 student,是通過賦值操作進行類型推論而推斷出來的。

如果需要我們手動給 student 添加類型,則應該是這樣:

1 let student:(x:string,y:number)=>string = function(x:string,y:number):string{
2     return `我是${x},今年${y}歲`;
3 }
4 
5 console.log(student("wzy",22)); // 我是wzy,今年22歲

這是就需要多談兩句了:

(1)前後參數名稱可以不一致

複製代碼

 1 let student:(x:string,y:number)=>string = function(name:string,age:number):string{
 2     return `我是${x},今年${y}歲`;
 3 }
 4 // 這樣對嗎?嗯!好像是哪裏不太對~
 5 // Cannot find name 'x'.   
 6 // Cannot find name 'y'.
 7 
 8 let student:(x:string,y:number)=>string = function(name:string,age:number):string{
 9     return `我是${name},今年${age}歲`;
10 }
11 // 這樣就對了~

複製代碼

(2)我對這種聲明函數的方式的理解就是:

let student : (x:string,y:number)=>string = function(name:string,age:number):string{}
  聲明變量             指定函數類型                     根據指定函數類型定義函數

此時再根據之前講過的“類型推論”,就有以下這樣的聲明方式了
1 let student:(x:string,y:number)=>string = function(name,age){
2     return `我是${name},今年${age}歲`;
3 }
4 5 let student = function(name:string,age:number):string{
6     return `我是${name},今年${age}歲`;
7 }

 

據說這叫“按上下文歸類”,是類型推論的一種。

(3)之前我們也講過,當沒有返回值的時候要怎樣,在這種完整的函數聲明的情況下是不可以返回值爲空的,這時我們就會想起void

複製代碼

 1 let student:(x:string,y:number) = function(name,age){  
 2     console.log(`我是${name},今年${age}歲`);
 3 }
 4 
 5 //  '=>' expected.
 6 // 這樣是不允許的,也不要妄想   =>""  這樣,人家就沒有""這種類型好嗎
 7 
 8 let student:(x:string,y:number)=>void = function(name,age){  
 9     console.log(`我是${name},今年${age}歲`);
10 }
11 // 還是老老實實的這樣子吧

複製代碼

3.接口中函數的定義

複製代碼

1 interface Student{
2     (x:string,y:number):string
3 }
4 
5 let str1:Student;
6 str1 = function(x,y){
7     return `我是${x},今年${y}歲`; 
8 }

複製代碼

4.可選參數

之前提到過函數的參數是不可多不可少的,但是也可以像接口那樣的形式用?表示可選參數

複製代碼

 1 function student(name:string,age:number,sex?:boolean){
 2     if(sex){
 3         return `name:${name},age:${age},sex:女`;
 4     }else{
 5         return `name:${name},age:${age},sex:未知`;
 6     }
 7 }
 8   
 9 console.log(student("weizeyang",22,true));   // name:weizeyang,age:22,sex:女
10 console.log(student("weizeyang",22));        // name:weizeyang,age:22,sex:未知

複製代碼

再看看它要注意哪些:

(1)可選參數要放在必需參數後面

(2)肚子好疼,不開心

  

5.默認參數

在typescript中,可以爲函數中的參數設置一個默認值,當用戶沒有傳這個參數或者傳的值是undefined時,就顯示默認值。

複製代碼

 1 function student(name:string,age:number,sex="女"){
 2     return `name:${name},age:${age},sex:${sex}`;
 3 }
 4   
 5 console.log(student("weizeyang",22,"未知"));  
 6 // name:weizeyang,age:22,sex:未知
 7 
 8 console.log(student("weizeyang",22));   
 9 // name:weizeyang,age:22,sex:女
10 
11 console.log(student("weizeyang",22,undefined));   
12 // name:weizeyang,age:22,sex:女

複製代碼

千古不無的注意點:

(1)可以看到,默認值的參數識別爲可選參數,但不必在必需參數後面。

(2)雖說它是可選參數了耶不必放在必需參數後面了,但是一旦放在必需參數前面,就要明確寫上undefined或者值,即不能空這。

這樣一來,寫在前面也就不算是可選參數了。

複製代碼

 1 function student(sex="女",name:string,age:number){
 2     return `name:${name},age:${age},sex:${sex}`;
 3 }
 4   
 5 console.log(student("未知","weizeyang",22));  
 6 // name:weizeyang,age:22,sex:未知
 7 
 8 console.log(student("weizeyang",22));   
 9 // Expected 3 arguments, but got 2.
10 
11 console.log(student(undefined,"weizeyang",22));   
12 // name:weizeyang,age:22,sex:女

複製代碼

不知道別人看得懂這句話不,反正我是看懂說的是啥了。

6.剩餘參數

 之前講過的必要參數、默認參數和可選參數表示的都是某一個參數,有時,你想同時操作多個參數,或者你並不知道會有多少個參數會傳進來。

在JavaScript中,你可以使用arguments來訪問所有傳入的參數。

在typescript裏,你可以把所有參數收集到一個變量裏:

1 function student(age:number,sex:boolean,...name:string[]){
2     return `我是${name.join("")},年齡${age}`;
3 } 
4 
5 console.log(student(22,true,"wei","ze","yang"));
6 // 我是weizeyang,年齡22

來吧,注意點:

(1)剩餘參數會被當做個數不限的可選參數,可以一個都沒有,也可以有任意個。同樣要放在必要參數後面。

(2)是數組類型,名字是省略號後面的字段名,可以在函數體內使用這個數組。

(3)當調用函數時,別傳入數組,依次傳入就行。

7.Lambda表達式和使用this

在JavaScript裏,this的值在函數被調用的時候纔會指定。學過JavaScript的都知道this很是讓人頭疼啊!

複製代碼

 1 var student = {
 2     names: ["wzy", "wyy", "wxy", "yz"],
 3     getName: function() {
 4         
 5         return function(){
 6             var item = Math.floor(Math.random() * 4);
 7             console.log(this);  
 8 
 9             return {
10                 name: this.names[item]    //  Cannot read property 'names' of undefined
11             }
12         }
13     }
14 }
15 
16 let sname = student.getName();
17 console.log("my name is " + sname().name);

複製代碼

7行打印出的是window,因爲這裏沒有對this進行動態綁定

複製代碼

 1 "use strict";   // 嚴格模式
 2 
 3 var student = {
 4     names: ["wzy", "wyy", "wxy", "yz"],
 5     getName: function() {
 6         
 7         return function(){
 8             var item = Math.floor(Math.random() * 4);
 9             console.log(this);    // undefined
10 
11             return {
12                 name: this.names[item]    //  Cannot read property 'names' of undefined
13             }
14         }
15     }
16 }
17 
18 let sname = student.getName();
19 console.log("my name is " + sname().name);

複製代碼

9行打印出的是undefined,看着和上面的好像也沒什麼差別,實則現在是在JavaScript嚴格模式下(咱們一直都在),打印出的就是undefined了。

爲了解決這個問題,我們可以在函數被返回時就綁好正確的this。 這樣的話,無論之後怎麼使用它,都會引用綁定的‘student’對象。

我們把函數表達式變爲使用lambda表達式( () => {} )。 這樣就會在函數創建的時候就指定了‘this’值,而不是在函數調用的時候。

複製代碼

 1 var student = {
 2     names: ["wzy", "wyy", "wxy", "yz"],
 3     getName: function() {
 4         console.log(this);
 5         // 注意:下面這行是一個lambda,允許我們提前捕捉它
 6         return () => {
 7             var item = Math.floor(Math.random() * 4);
 8             console.log(this);
 9             
10             return {
11                 name: this.names[item]
12             }
13         }
14     }
15 }
16 
17 let sname = student.getName();
18 console.log("my name is " + sname().name);

複製代碼

在第4,8行打印出了

即student這個對象,大功告成

8.重載

 舉個栗子:

複製代碼

 1 function schools(type:number|string):number|string{
 2     if(typeof type === "number"){
 3         return type.toFixed(2);
 4     }else if(typeof type === "string"){
 5         return type.substring(0,1);
 6     }
 7 }
 8 
 9 console.log(schools(1));       // 1.00
10 console.log(schools("wzy"));   // w

複製代碼

上面函數的參數和返回值的類型均使用了聯合類型的形式,這樣一來我們就不能準確的表達我傳入的是number類型的返回的就要是number類型得了

這時就要引入重載的概念了

在學Java的時候,重載是這樣講的:函數名相同,函數的參數列表不同(包括參數個數和參數類型),返回值類型可同可不同

網上的例子是這樣給出的:

複製代碼

 1 function schools(type:number):number;
 2 function schools(type:string):string;
 3 function schools(type:number|string):number|string{
 4     if(typeof type === "number"){
 5         return type.toFixed(2);
 6     }else if(typeof type === "string"){
 7         return type.substring(0,1);
 8     }
 9 }
10 
11 console.log(schools(1));       // 1.00
12 console.log(schools("wzy"));   // w

複製代碼

就是講這兩個函數在上面多聲明瞭幾次

需要注意的是:

(1)TypeScript 會優先從最前面的函數定義開始匹配,所以多個函數定義如果有包含關係,需要優先把精確的定義寫在前面。

再給個栗子:

複製代碼

 1 function schools(type:number):number;
 2 function schools(type:string):string;
 3 function schools(type:number | string | boolean):number | string | boolean{
 4     if(typeof type === "number"){
 5         return type.toFixed(2);
 6     }else if(typeof type === "string"){
 7         return type.substring(0,1);
 8     }else if(typeof type === "boolean"){
 9         return true;
10     }
11 }
12 
13 console.log(schools(1));       // 1.00
14 console.log(schools("wzy"));   // w
15 console.log(schools(true));    
16 //  Argument of type 'true' is not assignable to parameter of type 'string'.

複製代碼

(2)說明3行的代碼並不屬於重載部分,這裏只有1,2行的函數重載起作用。用除這兩種外的其他類型的參數調用schools會產生錯誤。

(3)1,2行是定義了兩個函數,而3行只是對這兩個重載函數的實現。

我在知乎上看到的總結是這樣說的,因爲會有好多人覺得typescript的重載存在的意義不大:

  TypeScript 的重載是爲了給調用者看,方便調用者知道該怎麼調用(同時 tsc 也會進行靜態檢查以避免錯誤的調用)。

  有助於IDE的代碼提示,以及返回值的類型約束。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章