Typescript 最佳實踐

来源:https://www.cnblogs.com/beidan/archive/2019/12/30/12118198.html
-Advertisement-
Play Games

文章列表: 《一》大話 TypeScript 基本類型 《二》大話 Typescript 枚舉 《三》大話 Typescript 介面 《四》大話 Typescript 泛型 《五》大話 Typescript 函數與類 《六》Typescript 最佳實踐 《一》大話 TypeScript 基本類型 ...


文章列表: 

 

為了更好的閱讀體驗,  可以看. 

 

一年前剛接觸 Typescript 的時候, 覺得它加大了代碼工作量. 寫一大堆東西.為了找某個類型東奔西跑, 引入第三庫還經常報錯. 

然而現在的我想說: 真香. 

我們經常吐槽別人代碼可維護性特別低, 總是希望別人能夠主動的寫註釋, 可是寫註釋卻沒有任何方式可以進行約束. 這下好了, 類型就是最好的註釋, 用 Typescript, 可以大大提高代碼的可維護性. 

 

 

一. 如何處理第三方庫類型相關問題

Typescipt 所提供的第三方庫類型定義不僅約束我們的輸入調用, 還能為我們提供文檔. 現在, NPM 上的第三方類型定義種類繁多,很難保證類型定義是正確的. 也很難保證所有使用的第三方庫都有類型定義. 

那麼, 在這個充滿未知的過程中,如何才能正確使用TypeScript中的第三方庫呢?

 

下麵列舉了四種常見的無法正常工作的場景以及對應的解決方法:

  • 庫本身沒有自帶類型定義

  • 庫本身沒有類型定義, 也沒有相關的@type

  • 類型聲明庫有誤

  • 類型聲明報錯

 

1. 庫本身沒有自帶類型定義

查找不到相關的庫類型. 舉個慄子 

在初次將 react 改造支持 typescript 時, 想必很多人都會遇到 module.hot 報錯. 此時只需要安裝對應的類型庫即可. 

安裝 @types/webpack-env

 

2. 庫本身沒有類型定義, 也沒有相關的@type

那隻能自己聲明一個了. 隨便舉個慄子. 

declare module "lodash"

 

3. 類型聲明庫有誤

  • 推動解決官方類型定義的問題, 提issue, pr 

  • Import 後通過 extends 或者 merge 能力對原類型進行擴展

  • 忍受類型的丟失或不可靠性

  • 使用 // @ts-ignore  忽略

 

4. 類型聲明報錯

  • 在 compilerOptions 的添加"skipLibCheck": true, 曲線救國

 

二. 巧用類型收縮解決報錯下麵列舉了幾種常見的解決方法:

  • 類型斷言

  • 類型守衛 typeof in instanceof 字面量類型保護

  • 雙重斷言

 

1、 類型斷言

類型斷言可以明確的告訴 TypeScript 值的詳細類型,

在某些場景, 我們非常確認它的類型, 即使與 typescript 推斷出來的類型不一致. 那我們可以使用類型斷言. 

語法如下: 

<類型>值

 

值 as 類型 // 推薦使用這種語法. 因為<>容易跟泛型, react 中的語法起衝突

 

舉個例子, 如下代碼,  padding 值可以是 string , 也可以是 number, 雖然在代碼裡面寫了 Array(), 我們明確的知道, padding 會被parseint 轉換成 number 類型, 但類型定義依然會報錯. 

 

function padLeft(value: string, padding: string | number) {

   // 報錯: Operator '+' cannot be applied to

   // types 'string | number' and 'number'

   return Array(padding + 1).join(" ") + value;

}

 

解決方法, 使用類型斷言. 告訴 typescript 這裡我確認它是 number 類型, 忽略報錯. 

function padLeft(value: string, padding: string | number) {

   // 正常

   return Array(padding as number + 1).join(" ") + value;

}

 

但是如果有下麵這種情況, 我們要寫很多個 as 麽? 

function padLeft(value: string, padding: string | number) {

   console.log((padding as number) + 3);

   console.log((padding as number) + 2);

   console.log((padding as number) + 5);

   return Array((padding as number) + 1).join(' ') + value;

}

 

2、 類型守衛

類型守衛有以下幾種方式, 簡單的概括以下

  • typeof:  用於判斷 "number","string","boolean"或 "symbol" 四種類型. 

  • instanceof : 用於判斷一個實例是否屬於某個類

  • in: 用於判斷一個屬性/方法是否屬於某個對象

  • 字面量類型保護

 

上面的例子中, 是 string | number 類型, 因此使用 typeof 來進行類型守衛. 例子如下: 

function padLeft(value: string,padding: string | number) {

   if (typeof padding === 'number') {

       console.log(padding + 3); //正常

       console.log(padding + 2); //正常

       console.log(padding + 5); //正常

        //正常

       return Array(padding + 1).join(' ') value;

   }

   if (typeof padding === 'string') {

       return padding + value;

   }

}

 

相比較 類型斷言 as , 省去了大量代碼. 除了 typeof , 我們還有幾種方式, 下麵一一舉例子. 

 

  • instanceof :用於判斷一個實例是否屬於某個類

class Man {

   handsome = 'handsome';

}

 

class Woman {

   beautiful = 'beautiful';

}

 

function Human(arg: Man | Woman) {

   if (arg instanceof Man) {

       console.log(arg.handsome);

       console.log(arg.beautiful); // error

   } else {

       // 這一塊中一定是 Woman

       console.log(arg.beautiful);

   }

}

 

  • in : 用於判斷一個屬性/方法是否屬於某個對象

interface B {

   b: string;

}

 

interface A {

   a: string;

}

 

function foo(x: A | B) {

   if ('a' in x) {

       return x.a;

   }

   return x.b;

}

 

  • 字面量類型保護

有些場景, 使用 in, instanceof, typeof 太過麻煩. 這時候可以自己構造一個字面量類型. 

type Man = {

   handsome: 'handsome';

   type: 'man';

 

};

 

type Woman = {

   beautiful: 'beautiful';

   type: 'woman';

};

 

function Human(arg: Man | Woman) {

   if (arg.type === 'man') {

       console.log(arg.handsome);

       console.log(arg.beautiful); // error

   } else {

       // 這一塊中一定是 Woman

       console.log(arg.beautiful);

   }

}

 

3、雙重斷言

有些時候使用 as 也會報錯,因為 as 斷言的時候也不是毫無條件的. 它只有當S類型是T類型的子集,或者T類型是S類型的子集時,S能被成功斷言成T. 

所以面對這種情況, 只想暴力解決問題的情況, 可以使用雙重斷言. 

function handler(event: Event) {

   const element = event as HTMLElement;

   // Error: 'Event' 和 'HTMLElement'

    中的任何一個都不能賦值給另外一個

}

 

如果你仍然想使用那個類型,你可以使用雙重斷言。首先斷言成相容所有類型的any

 

function handler(event: Event) {

   const element = (event as any) as HTMLElement;

    // 正常

}

 

三. 巧用 typescript 支持的 js 最新特性優化代碼

1. 可選鏈 Optional Chaining 

 

let x = foo?.bar.baz();

 

 

typescript 中的實現如下: 

var _a;

let x = (_a = foo) === null ||

_a === void 0 ? void 0 : _a.bar.baz();

 

利用這個特性, 我們可以省去寫很多噁心的 a && a.b && a.b.c 這樣的代碼

 

2. 空值聯合 Nullish Coalescing

 

let x = foo ?? '22';

 

 

typescript 中的實現如下: 

 

let x = (foo !== null && foo !== void 0 ?

foo : '22');

 

 

 

四. 巧用高級類型靈活處理數據typescript 提供了一些很不錯的工具函數. 如下圖

 

 

 

 

  • 類型索引

為了實現上面的工具函數, 我們需要先瞭解以下幾個語法: 

keyof : 獲取類型上的 key 值

extends : 泛型裡面的約束

T[K] : 獲取對象 T 相應 K 的元素類型

 

type Partial<T> = {

   [P in keyof T]?: T[P]

}

 

在使用 props 的時候, 有時候全部屬性都是可選的, 如果一個一個屬性寫 ? , 大量的重覆動作. 這種時候可以直接使用 Partial<State>  

 

Record 作為一個特別靈活的工具. 第一個泛型傳入對象的key值, 第二個傳入 對象的屬性值. 

type Record<K extends string, T> = {

   [P in K]: T;

}

 

我們看一下下麵的這個對象, 你會怎麼用 ts 聲明它? 

const AnimalMap = {

   cat: { name: '貓', title: 'cat' },

   dog: { name: '狗', title: 'dog' },

   frog: { name: '蛙', title: 'wa' },

};

 

此時用 Record 即可. 

type AnimalType = 'cat' | 'dog' | 'frog';

 

interface AnimalDescription {

name: string, title: string

}

 

const AnimalMap:

Record<AnimalType, AnimalDescription> = {

   cat: { name: '貓', title: 'cat' },

   dog: { name: '狗', title: 'dog' },

   frog: { name: '蛙', title: 'wa' },

};

 

  • never, 構造條件類型

除了上面的幾個語法. 我們還可以用 never , 構造條件類型來組合出更靈活的類型定義. 

語法: 

never: 從未出現的值的類型

 

// 如果 T 是 U 的子類型的話,那麼就會返回 X,否則返回 Y

構造條件類型 : T extends U ? X : Y

 

type Exclude<T, U> = T extends U ? never : T;

 

// 相當於: type A = 'a'

type A = Exclude<'x' | 'a', 'x' | 'y' | 'z'>

 

  • 更簡潔的修飾符: - 與 + 

可以直接去除 ? 將所有對象屬性變成必傳內容. 

type Required<T> = { [P in keyof T]-?: T[P] };

 

// Remove readonly

type MutableRequired<T> = {

    -readonly [P in keyof T]: T[P]

};  

 

  • infer: 在 extends 條件語句中待推斷的類型變數。 

// 需要獲取到 Promise 類型里蘊含的值

type PromiseVal<P> =

P extends Promise<infer INNER> ? INNER : P;

 

type PStr = Promise<string>;

 

// Test === string

type Test = PromiseVal<PStr>;

 

 

五. 辨別 type & interface 

在各大類型庫中, 會看到形形色色的 type 和 interface . 然而很多人在實際中卻不知道它們的區別. 

 

官網的定義如下: 

 

An interface can be named in an extends or implements clause, but a type alias for an object type literal cannot.

 

An interface can have multiple merged declarations, but a type alias for an object type literal cannot.

 

從一張圖看出它們兩的區別: 

 

 

 

 

建議:  能用 interface 實現,就用 interface , 如果不能才用 type. 

 

 

 

為了更好的閱讀體驗,  《typescrit 最佳實踐》

- 歡迎關註「前端加加」,認真學前端,做個有專業的技術人...

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Css3文本與字體 文本陰影 h1 { text-shadow: 5px 5px 5px red; } word-break換行: h1:nth-child(1) { word-break: normal; } /*英文:一行放不下時整個單詞換行*/ h1:nth-child(2) { word-b ...
  • HTTP狀態碼的英文為 HTTP Status Code。下麵是常見的HTTP狀態碼: 200 - 請求成功 301 - 資源(網頁等)被永久轉移到其它URL 404 - 請求的資源(網頁等)不存在 500 - 內部伺服器錯誤 1、HTTP狀態碼的分類 HTTP狀態碼由三個十進位數字組成,第一個十進 ...
  • 如需代碼,詳情請咨詢郵箱,謝謝配合 ...
  • Css3選擇器相關: section > div直接子元素選擇器 div + article相鄰兄弟選擇器(在元素之後出現) div ~ article通用兄弟選擇器(在元素之後出現) 屬性選擇器: a[href] { text-decoration: none; } a[href="#"] { c ...
  • $("#divSetting").on("click", function () { $(this).toggleClass("open"); }); ...
  • 遇到一個小問題,記錄一下 問題:在微信小程式中使用scroll-view標簽時,用height:cale(xx - xx)設置高度無效,在page中設置高度為百分百依舊無效 解決辦法:直接在最大的view標簽中設置高度為百分百即可 ...
  • 有一段時間沒有更新技術博文了,因為這段時間埋下頭來看Vue源碼了。本文我們一起通過學習雙向綁定原理來分析Vue源碼。預計接下來會圍繞Vue源碼來整理一些文章,如下。 "一起來學Vue雙向綁定原理 數據劫持和發佈訂閱" "一起來學Vue模板編譯原理(一) Template生成AST" "一起來學Vue ...
  • 本文我們一起通過學習Vue模板編譯原理(一) Template生成AST來分析Vue源碼。預計接下來會圍繞Vue源碼來整理一些文章,如下。 "一起來學Vue雙向綁定原理 數據劫持和發佈訂閱" "一起來學Vue模板編譯原理(一) Template生成AST" "一起來學Vue模板編譯原理(二) AST ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...