TypeScript 中高級類型的理解?有哪些?
- 2021 年 9 月 13 日
- 筆記
- typescript
一、是什麼
除了string
、number
、boolean
這種基礎類型外,在 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
的高階用法,在實踐場景中,還有更多更複雜的組合,需要在實踐中慢慢體會