概述
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 中,null
和undefined
與其它類型是同層級的,它們不是任何類型的子類型,也就是說我們不能用它們來表示一個特定類型的空指針。但是我們確實需要在一個變量未確定時保持爲空狀態,這時可以用聯合類型來表示一個變量可爲null
或undefined
。示例:
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 } }
類型A
和B
有同名但類型不同的成員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 類型。