【Typescript小小冊】高級類型

概述

Typescript 的高級類型指操作基本類型和複合類型而得的類型,包括:

  • 聯合
  • 交叉
  • 泛型

類型別名

在講具體的高級類型之前,我們先了解一下 Typescript 的類型別名。類型別名也是一種類型,用一個單詞代表可能比較複雜的類型聲明,用關鍵字type表示。

示例:

type S = string

let a: S = 'a'

這裏用S作爲string的別名,使用方法和string一模一樣。

別名不僅可以代表基本類型,它可以代表任意類型。示例:

type SA = string[] // 代表字符串數組
type Handler = (x: string) => void // 代表函數
type I = {
  // 代表接口
  name: string
  value: number
}
class C {
  name: string = 'a'
  value: number = 0
}
type D = C // 代表類

類型

聯合

聯合類型是指變量爲多個類型中的一種,是“或”的關係,用操作符|表示。

示例:

type StringOrNumber = string | number

let a: StringOrNumber = 'a'
let b: StringOrNumber = 0

function log(x: string | number) {
  console.log(x)
}

這裏StringOrNumber表示一種可以是字符串或者也可以是數字的類型,所以對於StringOrNumber類型的變量,既可以賦值爲字符串,也可以賦值爲數字。

在 Typescript 中,nullundefined與其它類型是同層級的,它們不是任何類型的子類型,也就是說我們不能用它們來表示一個特定類型的空指針。但是我們確實需要在一個變量未確定時保持爲空狀態,這時可以用聯合類型來表示一個變量可爲nullundefined。示例:

interface A {
  name: string
}

let a: A | null = null

字面量類型

Typescript 中字面量也可以作爲一個類型。比如:

let a: 'A'

上述聲明表示變量a的值只能是'A',賦值爲其它值時會報錯。

數字和布爾也可以作爲字面量類型。這看起來沒什麼用,但是這種特性一般會與聯合結合起來使用。比如:

type Option = 'A' | 'B' | 'C' | 'D'

let a: Option = 'A'
let e: Option = 'E' // 錯誤

Option類型的變量只能被賦值爲'A''B''C''D'其中一個值。這一種用法和枚舉很像,比如:

enum Option {
  A,
  B,
  C,
  D
}

不同的是,字面量類型本質上是字符串,可以使用字符串的所有方法。而且,通過keyof操作符,我們可以動態的生成字面量類型,這一點將在之後介紹。

交叉

交叉類型是指多個類型合併成一個類型,是“且”的關係,用操作符&表示。

示例:

interface A {
  x: string
  y: number
}
interface B {
  x: string
  z: number
}

type C = A & B

let c: C = { x: 'x', y: 0, z: 1 }

交叉類型的成員包含了所有原類型的的所有成員,比如上述代碼中,c變量必須既是A類型也是B類型,也就是說它必須同時擁有兩個類型要求的屬性。

有一些類型是無法交叉的,比如基本類型。示例:

type A = string & number

因爲一個變量不可能既是字符串又是數字,所以最終A類型是never類型。

如果被交叉的兩個類型有同名但類型不同的成員,那麼這兩個同名成員也會被交叉。示例:

interface A {
  x: { a: string; b: number }
}
interface B {
  x: { a: string; c: number }
}

type C = A & B

let c: C = { x: { a: 'a', b: 0, c: 1 } }

類型AB有同名但類型不同的成員x,交叉時兩個x成員也可以交叉,因此最終x的類型爲:{a: string, b: number, c: number}

泛型

Typescript 中的泛型與其它面嚮對象語言中的泛型和相似,包括:泛型函數、泛型類和泛型接口。

泛型函數

示例:

function merge<T, U>(x: T, y: U): { x: T; y: U } {
  let t: { x: T; y: U } = { x, y }

  return t
}

merge<string, number>('a', 0)

我們可以在中括號內聲明函數中使用到的泛型變量,它代表了在調用函數時傳入的類型。類型變量T可以用在任何需要類型聲明的地方,比如參數類型、返回值類型、局部變量類型等。

箭頭函數也可以包含泛型。示例:

const merge = <T, U>(x: T, y: U): { x: T; y: U } => {
  let t: { x: T; y: U } = { x, y }

  return t
}

泛型類

示例:

class Merge<T, U> {
  x: T
  y: U

  constructor(x: T, y: U) {
    this.x = x
    this.y = y
  }

  merge(): { x: T; y: U } {
    let t: { x: T; y: U } = { x: this.x, y: this.y }

    return t
  }
}

let merge = new Merge<string, number>()

我們可以在中括號內聲明類中使用到的泛型變量,它代表了在實例化類時傳入的類型。類型變量T可以用在任何需要類型聲明的地方,比如成員、構造方法參數類型等。

泛型接口

示例:

interface Merge<T, U> {
  x: T
  y: U
}

let merge: Merge<string, number> = { x: 'a', y: 0 }

我們可以在中括號內聲明接口中使用到的泛型變量,它代表了在聲明變量類型時傳入的類型。類型變量T可以用在任何需要類型聲明的地方,比如成員、方法參數類型等。

泛型約束

由於在泛型類型中,泛型變量是在使用時傳入的,所以在編譯時無法得知其具體類型,那麼讀取類類型變量的任何成員都是不行的。比如:

function scale<T>(x: T): number {
  return x.length // 錯誤
}

雖然我們可能心裏知道在使用時我們會傳入一個具有length成員的變量(比如字符串),但是在編譯時編譯器並不知道。因此,我們需要告訴編譯器T類型是那一類具有length成員的類型。使用extends關鍵字:

interface HasLength {
  length: number
}

function scale<T extends HasLength>(x: T): number {
  return x.length
}

scale('abc')

我們使用extends對類型變量T進行了約束,它必須具有length屬性。也就是是說T類型必須實現或繼承了 HasLength 類型。

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