TypeScript防脫髮級入門——基本型別檢查

法醫發表於2022-01-04
嗨!大家好!我是法醫,一隻治療系前端碼猿?,與程式碼對話,傾聽它們心底的呼聲,期待著大家的點贊?與關注➕。

1. 如何進行型別約束

型別約束其實很簡單,只需要在變數函式引數函式返回值位置上加上 :型別 就可以了。

舉個例子 ?: 變數

//我們定義變數的時候,肯定是知道這個變數是存放什麼型別的資料

let name:string = "法醫";

一旦給 name 賦值其它型別,立馬會提示錯誤

image.png

舉個例子 ?: 函式引數和返回值

//引數後面 :number表示傳的引數必須是數字型別,而test函式後面的 :number表示返回值是數字型別
function test(a:number,b:number):number {
    return a + b;
}
test(1,2);//當呼叫test函式傳值為數字表示可以正常執行,傳其它型別則會報錯

傳入字串就會報錯

image.png

當我們寫一個函式的時候,我們非常清楚函式的引數以及返回值是什麼型別的,此時我們可以約束好型別,在之後的呼叫中我們可以放心的呼叫函式,因為只要寫錯了,立馬會提示錯誤,不需要等到程式執行後再提示錯誤,這些在JS中是做不到的,但是在TS中很輕鬆可以做到,不僅如此,型別檢查還帶來很多好處,比方說:

舉個例子?:

JS中我們是沒有辦法確定下面程式碼中text(1,2)呼叫的就是一個函式,中途有可能test會被修改,然後呼叫函式就會報錯

function test(a,b) {
    return a + b;
}
// 很多行程式碼
test = 123;
// 很多行程式碼
test(1,2);

image.png

但是在TS中這種情況絕對是不允許的 ?

image.png

由於TS知道函式test和呼叫函式test是同一個東西,於是就出現一個神奇的效果——當需要給函式重新命名的時候,雙擊函式test並且按F2,函式名改了,呼叫函式名也跟著改了,之所以會達到這個效果,是因為TS有嚴格的型別檢查系統,它知道呼叫函式的test用的就是test函式,它們兩者之間是建立聯絡的

GIF 2021-9-7 15-34-20.gif

不僅如此,還有一種效果:當我們點選呼叫函式並且按F12,它會跳到定義的函式位置,

GIF 2021-9-7 15-43-12.gif

為了讓我們少寫點程式碼,使用TS進行約束的時候,TS在很多場景中可以完成型別推導

舉個例子?:

當我們把函式返回值約束去掉以後依然可以從提示中發現返回值是number,這是因為我們將引數約束為number,數字與數字相加依然是數字,所以最後函式也會返回number,賦值給變數result,TS還會智慧地發現函式返回的結果是number,所以result型別也是number,因此我們只需要在引數位置加上型別約束就可以了,TS在每個地方都有型別檢查,是不是很牛逼?

GIF 2021-9-8 12-58-32.gif

? 那問題來了:我怎麼知道這型別推導什麼時候能推導成功,什麼時候推導失敗呢?

? 解答:

有個小技巧,當我們看到變數或者函式的引數出現三個小點,這三個點就是在提醒:你給我當心點,我確實做不到了,表示當前沒有推匯出來到底是什麼型別,可以用any型別來表示,這時就需要手動約束一下,

image.png

any:表示任意型別,對該型別,TS不進行型別檢查

2. 基本型別

注意是首字母是小寫

  • number:數字,

    let figure:number = 6;
  • string:字串

      let user:string = "法醫";
  • boolean:布林值

      let fake:boolean = false;
  • array:陣列

    :number[]這種寫法其實是語法糖,真正的寫法是下面第二種,這兩種寫法都可以約束陣列的,看個人喜好,建議使用第一種,因為在react中尖括號表示元件,Array<number>可能會造成衝突

      let arr:number[] = [1,2,3];
    
      let arr:Array<number> = [1,2,3];
  • object:物件

    object約束不是很常用,因為object約束力不是很強,它只能約束一個物件,卻不能約束物件裡面的內容,但是有時會用到

    //傳入一個物件,輸出value值
      function getValues(obj:object) {
       let vals = Object.values(obj);
       console.log(vals); // 輸出 法醫  18   
      }
      getValues({
       name:"法醫",
       age:18
      })
  • null和undefined

    nullundefined需要重點說一下,nullundefined是所有其他型別的子型別,它們可以賦值給其它型別,但是又會發生隱患,下面方法呼叫都會報錯,由於約束了是stringnumber,但是值又是nullundefined,這種情況是我們不希望發生的。

      let str:string = null;
      let nums:number = undefined;
      
      //下面都會報錯,由於約束了是string和number,但是值又是null和undefined
      str.toLocaleUpperCase();
      nums.toString();

    ? 解決方案:

    tsconfi.json配置檔案中加上:"strictNullChecks": true之後可以獲得更加嚴格的空型別檢查,nullundefined就不能賦值給其它型別了,只能賦值給自身

    image.png

3. 其它常用型別

  • 聯合型別:多種型別任選其一

    當一個變數既可以為字串又可以為undefined的時候就可以使用聯合型別,它可以配合使用型別保護進行判斷

    ? 型別保護:當對某個變數進行型別判斷之後,在判斷的語句中便可以確定它的確切型別,tyoeof可以觸發型別保護,但是它只能觸發簡單基本的型別保護,複雜型別是沒有辦法觸發的
    let user:string | undefined;
    
    if(typeof user === "string"){
        //型別保護,當進入這個判斷,TS一定會知道,此時user一定是字串
    }
  • viod型別:通常用於約束函式返回值,表示該函式沒有任何返回

    viodJs也是有的,表示運算一個表示式之後返回undefined,但在TS意思是不同的,通常用於約束函式返回值,表示該函式沒有任何返回

    function user():void{
        console.log("法醫");
    }

    當然不約束也是可以的,因為會型別推導出來

    image.png

  • never型別:通常用於約束函式返回值,表示該函式永遠不可能結束

    function thorwError(msg:string) {
        throw new Error(msg)
    }

    這個函式的型別推導是有問題的,推導的型別是viod,因為它永遠不會結束,型別應該是never而不是viod,所以需要手動更改

    GIF 2021-9-8 16-37-29.gif

      function thorwError(msg:string):never {
       throw new Error(msg)
      }

    由於是永遠不會結束,所以,下面的log函式無法執行,無法訪問程式碼

    image.png

    還有一種情況也是永遠不會結束,需要手動約束

    GIF 2021-9-8 16-53-09.gif

  • 字面量型別:使用一個進行約束,而不是型別約束

    //表示從此以後,變數name只能是 “法醫”,別的就會報錯
    let name:"法醫";

    GIF 2021-9-8 17-18-55.gif

    一般我們可以用字面量型別對性別或者物件中的屬性進行約束:

    //對gender 變數進行約束,只能是男或女,其它不行
    let gender :"男" | "女";
    
    //對users物件中的name和age屬性分別約束為字串和數字,下次給users賦值的時候,只能包含name和age
    let users:{
        name:string
        age:number
    }
  • 元組型別(Tuple):用的不多,瞭解一下就可以了,表示固定長度的陣列,並且陣列中的每一項型別確定

    //定義了一個變數為tupleType的陣列,這個陣列只能有兩項,並且第一個必須為字串,第二個必須為數字
    let tupleType:[string,number];
    
    //第一項必須為字串,第二項必須為數字,只能有兩項,否則報錯
    tupleType = ["法醫",5];
  • any型別:any型別可以繞過型別檢查,因此any型別可以賦值給任意型別,但肯定是有隱患的,因為它無法使用TS提供的保護機制,所以不建議隨意的使用any型別,為了解決any帶來的問題,TS3.0引入了unknown型別

    GIF 2021-9-8 19-19-54.gif

4. 型別別名

給已知的型別起個新的名字,防止重複書寫一些程式碼

type Gender = "男" | "女";
type user = {
    name:string
    age:number
    gender:Gender
}

function getUser(g:Gender) {
    //...
}

5. 函式的相關約束

  • 函式過載

先看一個函式combine,功能是如果傳遞兩個數字作為引數的時候相乘,傳遞兩個字串的時候相加,不相同都會報錯。

function combine(a:number | string,b:number | string):number | string {
    if(typeof a === "number" && typeof b === "number"){
        return a * b;
    }
    else if(typeof a === "string" && typeof b === "string"){
        return a + b;
    }
    throw new Error("a和b必須是相同的型別")
}

函式本身沒有什麼問題,問題就發生在函式呼叫的過程中,當我們程式碼寫多了以後,我們也許會失誤傳遞不同的型別作為引數,更可怕的是如果引數是函式的返回結果,那就更蒙了,因此,在函式的呼叫過程中最好告訴呼叫函式,要麼都是數字型別,要麼都是字串型別。

GIF 2021-9-8 20-57-48.gif

從邏輯上來說,都是數字的話返回的結果就是數字型別,都是字串的話返回的結果就是字串型別,然而result的型別是string | number,上圖可以清晰看到,這種情況,後面就沒有辦法使用result變數了,因為明明知道都是數字返回的結果一定是數字型別,都是字串返回的一定是字串型別。意味著程式碼提示中不會出現所有數字擁有的方法或者所有字串所擁有的方法,只會提示數字和字串共同擁有的方法——toStringvalueOf如下圖:

GIF 2021-9-8 21-06-21.gif

? 解決方案:

加上下面兩句程式碼,這兩句程式碼相當於告訴 TS combine函式只能有兩種情況,一種是兩個數字返回數字,另一種是兩個字串返回字串,這兩句程式碼就叫函式過載

? 函式過載:在函式實現之前,對函式呼叫的多種情況進行宣告。
//加上這兩句程式碼
/**
 * 得到a * b的結果
 * @param a 
 * @param b 
 */
function combine(a:number,b:number):number;
/**
 * 得到a + b的結果
 * @param a 
 * @param b 
 */
function combine(a:string,b:string):string;

function combine(a:number | string,b:number | string):number | string {
    if(typeof a === "number" && typeof b === "number"){
        return a * b;
    }
    else if(typeof a === "string" && typeof b === "string"){
        return a + b;
    }
    throw new Error("a和b必須是相同的型別")
}

let result = combine("b","n");

使用函式過載之後,當呼叫函式的時候只能傳兩個數字或者兩個字串,否則會報錯,再來看看效果:

GIF 2021-9-8 21-23-10.gif

  • 可選引數
? 可選引數:可以在某些引數名後面加上號,表示該引數可以不用傳遞。可選引數必須要在引數列表的末尾

image.png

當形參為三個,呼叫函式卻傳了兩個,就會報錯,TS是很嚴格的,不允許引數數量不匹配。假設第三個引數可以不傳遞,加個號表示是可選引數

image.png

相關文章