探索 TypeScript 類型註解 - 類型編程

Exploring TypeScript Type Annotations - Type Programminghtml

本文由 WowBar 團隊首發於 GitHubgit

做者: zhilidaligithub

歡迎來到 《探索 TypeScript 類型註解》 系列教程。算法

上一篇介紹了 TS 的高級類型。 本篇將前面的知識點融會貫通,將對類型的探索提高一個層次:從類型層面進行編程。typescript

目錄

類型編程

首先,咱們回顧一下前幾節對類型的探索。編程

  • 經過數據類型,咱們瞭解到 TS 支持的數據類型。
    • 支持 JS 數據類型。
    • 新增一些數據類型。
  • 經過自定義類型,咱們瞭解到如何聲明或命名一個類型。
    • 接口, interface 來聲明各類複雜數據類型。
    • 類型別名, type 對類型進行命名。
    • 泛型,<T> 如同 JS 中函數將類型做爲參數傳遞。
  • 經過類型檢查,咱們瞭解到類型與 JS 之間的各類交互。
    • 類型推斷,經過 JS 的值推斷出相應的類型。
    • 類型斷言,直接對 JS 中的值指定一個類型。
    • 類型兼容,由類型之間的關係反映出 JS 值之間的關係。
    • 類型保護,經過 JS 中的操做符來反映出精確的類型。
  • 經過高級類型,咱們瞭解到 TS 提供的各類類型操做符。
    • 交叉類型,使用類型操做符 &
    • 聯合類型,使用類型操做符
    • 索引類型,使用類型操做符 keyofT[K]
    • 映射類型,使用類型操做符 in
    • 條件類型,使用類型操做符 T extends U ? X : Y infer
    • 另外還有用於類型保護的類型謂詞中的 is 操做符以及上面所介紹的 typeof

根據 TS 提供的 數據類型 以及聲明的自定義類型,結合高級類型中的 操做符 能夠對類型進行各類運算 (高級類型本質上就是各類操做符表達式)。數據結構

再加上具備函數功能的 泛型,能夠對類型的運算進行封裝、複用、組合。要知道函數是 JS 中最強大的武器,誰說「類」來着,算了,好累,我還要(搬磚)拯救世界。還要知道,TS 沒有采用傳統面嚮對象語言使用的名義類型,而是基於偏向於函數式編程的結構類型,(JS 是多範式編程語言)。編程語言

到此爲止,咱們已經具有了對類型進行編程的各類工具 (程序 = 數據結構 + 算法),接下來各位童鞋就能夠發揮無窮的智慧了。函數式編程

童鞋請留步!俗話說吃人嘴短,拿人手軟,請先讓巨硬(微軟大大)炫個富。下面介紹 TypeScript 官方標準庫中封裝的實用工具類型。函數

實用工具類型

TypeScript 提供的實用工具類型用來實現常見的類型轉換,這些類型工具函數是全局可見的。

Extract,Exclude,NonNullable

  • Extract<T, U> :從 T 中提取能夠賦值給 U 的類型
  • Exclude<T, U> :從 T 中排除能夠賦值給 U 的類型
  • NonNullable<T> :從 T 中排除 nullundefined

使用示例

type foo = Extract<number | string, string>; // string
type bar = Exclude<number | string, string>; // number
type baz = NonNullable<number | string | null | undefined>; // string | number
複製代碼

具體實現

// 主要使用條件類型 `T extends U ? X : Y` 實現
type Extract<T, U> = T extends U ? T : never;
type Exclude<T, U> = T extends U ? never : T;
type NonNullable<T> = T extends null | undefined ? never : T;
複製代碼

Partial, Require, Readonly

  • Partial<T>:將 T 中的全部屬性設置爲可選
  • Require<T>:將 T 中的全部屬性設置爲必選
  • Readonly<T>:將 T 中的全部屬性設置爲只讀

使用示例

interface Type { a: number, b?: string };
let foo: Partial<Type> = { b: 'b' };
let bar: Required<Type> = { a: 1 }; // Error
let baz: Readonly<Type> = { a: 1 };
baz.a = 2; // Error
複製代碼

具體實現

// 主要使用映射類型 `[K in T]: Type` 及索引類型 `keyof T`、`T[P]` 實現
type Partial<T> = { [P in keyof T]?: T[P] };
type Require<T> = { [P in keyof T]-?: T[P] }; // 注意這裏的 `-?`
type Readonly<T> = { readonly [P in keyof T]: T[p] };
複製代碼

TypeScript 標準庫中提供了許多實用的工具類型,並且隨着 TypeScript 不斷更新迭代,會有更多的實用工具類型加入到標準庫中,此處不在重複介紹(提示:實用工具很實用),詳情請移步官方手冊,手冊中給出了詳細的使用示例。對於這些工具類型的具體實現,請移步官方倉庫的 lib

typeof

在 TS 中,還能夠使用 typeof 來獲取變量的類型。

let foo: number = 3;
type bar = typeof foo; // 至關於 type bar = number
複製代碼

extends

前面的章節中多處使用了 extends 關鍵字。以下

原生 JS 中類的繼承

class A { a: number }
class B extends A { b: string } // B 繼承 A
let a: A = new A();
let b: B = new B();

a = b; // Ok, A = B 少兼容多,子類兼容超類
複製代碼

接口繼承

interface A { a: number }
interface B extends A { b: string }
let a: A = { a: 1 };
let b: B = { b: 'b', a: 1 };

a = b; // Ok, A = B
複製代碼

泛型約束

interface A { a: number }

let foo: <B extends A>(arg: B) => void;
foo({ a: 1, b: 2});
複製代碼

條件類型

interface A { a: number }
interface B { a: number, b: string }
type E = B extends A ? true : false;
// type E = true
複製代碼

彙總以下

  • 類的繼承: class SubClass extends SupClass
  • 接口繼承:interface SubType extends SupType {}
  • 泛型約束:<T extends U>
  • 條件類型:T extends U ? X : Y

以上均有共同的形式 Sub extends Sup

  1. extends 關鍵字的語義:它們之間屬於繼承關係,即子類(型)繼承超類(型)。
  2. 從類型兼容性角度:超類型兼容子類型,即子類型能夠賦給超類型。
  3. 從功能上:
    • 類和接口中的 extends 用來定義,可有多個超類 Sup,中間用 , 分割。
    • 泛型約束和條件類型中的 extends 用來檢測兼容性,即 Sup 是否兼容 Sub

類型與集合

既然是編程,下面從數學的角度來簡單粗略地描述 TS 類型系統,(讀者可略過,想深刻的童鞋可移步:zhuanlan.zhihu.com/p/38081852)。

TS 中的類型比如數學中的集合,類型是具備某種特定性質的 JS 值的集合。 好比 number 類型對應 JS 中全部數值的集合。

類型集合的分類

  • any 類型對應爲 全集
  • never 類型對應爲 空集
  • 聯合類型是類型集合之間的 並集
  • 交叉類型是類型集合之間的 交集

結語

本篇主要是對類型進行編程的能力進行了梳理。

協議

CC BY-NC-ND 4.0
LICENSE

本做品採用知識共享署名-非商業性使用-禁止演繹 4.0 國際許可協議進行許可。

《探索 TypeScript 類型註解》

  1. [x] 擴展的 JavaScript
  2. [x] 數據類型
  3. [x] 自定義類型
  4. [x] 類型檢查
  5. [x] 高級類型
  6. [x] 類型編程

參考連接

相關文章
相關標籤/搜索