TypeScript 中高階型別的理解?有哪些?

喆星高照發表於2021-09-13

 

 

一、是什麼

除了stringnumberboolean 這種基礎型別外,在 typescript 型別宣告中還存在一些高階的型別應用

這些高階型別,是typescript為了保證語言的靈活性,所使用的一些語言特性。這些特性有助於我們應對複雜多變的開發場景

二、有哪些

常見的高階型別有如下:

  • 交叉型別
  • 聯合型別
  • 型別別名
  • 型別索引
  • 型別約束
  • 對映型別
  • 條件型別

交叉型別

通過 & 將多個型別合併為一個型別,包含了所需的所有型別的特性,本質上是一種並的操作

語法如下:

T & U

適用於物件合併場景,如下將宣告一個函式,將兩個物件合併成一個物件並返回:

function extend<T , U>(first: T, second: U) : T & U {
    let result: <T & U> = {}
    for (let key in first) {
        result[key] = first[key]
    }
    for (let key in second) {
        if(!result.hasOwnProperty(key)) {
            result[key] = second[key]
        }
    }
    return result
}

聯合型別

聯合型別的語法規則和邏輯 “或” 的符號一致,表示其型別為連線的多個型別中的任意一個,本質上是一個交的關係

語法如下:

T | U

例如 number | string | boolean 的型別只能是這三個的一種,不能共存

如下所示:

function formatCommandline(command: string[] | string) {
  let line = '';
  if (typeof command === 'string') {
    line = command.trim();
  } else {
    line = command.join(' ').trim();
  }
}

型別別名

型別別名會給一個型別起個新名字,型別別名有時和介面很像,但是可以作用於原始值、聯合型別、元組以及其它任何你需要手寫的型別

可以使用 type SomeName = someValidTypeAnnotation的語法來建立型別別名:

type some = boolean | string

const b: some = true // ok
const c: some = 'hello' // ok
const d: some = 123 // 不能將型別“123”分配給型別“some”

此外型別別名可以是泛型:

type Container<T> = { value: T };

也可以使用型別別名來在屬性裡引用自己:

type Tree<T> = {
    value: T;
    left: Tree<T>;
    right: Tree<T>;
}

可以看到,型別別名和介面使用十分相似,都可以描述一個物件或者函式

兩者最大的區別在於,interface只能用於定義物件型別,而 type 的宣告方式除了物件之外還可以定義交叉、聯合、原始型別等,型別宣告的方式適用範圍顯然更加廣泛

型別索引

keyof 類似於 Object.keys ,用於獲取一個介面中 Key 的聯合型別。

interface Button {
    type: string
    text: string
}

type ButtonKeys = keyof Button
// 等效於
type ButtonKeys = "type" | "text"

型別約束

通過關鍵字 extend 進行約束,不同於在 class 後使用 extends 的繼承作用,泛型內使用的主要作用是對泛型加以約束

type BaseType = string | number | boolean

// 這裡表示 copy 的引數
// 只能是字串、數字、布林這幾種基礎型別
function copy<T extends BaseType>(arg: T): T {
  return arg
}

型別約束通常和型別索引一起使用,例如我們有一個方法專門用來獲取物件的值,但是這個物件並不確定,我們就可以使用 extends 和 keyof 進行約束。

function getValue<T, K extends keyof T>(obj: T, key: K) {
  return obj[key]
}

const obj = { a: 1 }
const a = getValue(obj, 'a')

對映型別

通過 in 關鍵字做型別的對映,遍歷已有介面的 key 或者是遍歷聯合型別,如下例子:

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

interface Obj {
  a: string
  b: string
}

type ReadOnlyObj = Readonly<Obj>

上述的結構,可以分成這些步驟:

  • keyof T:通過型別索引 keyof 的得到聯合型別 'a' | 'b'
  • P in keyof T 等同於 p in 'a' | 'b',相當於執行了一次 forEach 的邏輯,遍歷 'a' | 'b'

所以最終ReadOnlyObj的介面為下述:

interface ReadOnlyObj {
    readonly a: string;
    readonly b: string;
}

條件型別

條件型別的語法規則和三元表示式一致,經常用於一些型別不確定的情況。

T extends U ? X : Y

上面的意思就是,如果 T 是 U 的子集,就是型別 X,否則為型別 Y

三、總結

可以看到,如果只是掌握了 typeScript 的一些基礎型別,可能很難遊刃有餘的去使用 typeScript,需要了解一些typescript的高階用法,在實踐場景中,還有更多更復雜的組合,需要在實踐中慢慢體會

相關文章