TypeScript的索引類型和映射類型

文章首發於:github.com/USTB-musion…html

寫在前面

在ts中,索引類型和映射類型是相對複雜的內容。使用索引類型,編譯器就可以檢查使用了動態屬性名的代碼,而使用映射類型,能夠將舊類型轉化成新類型。下面用一篇文章來稍微深刻介紹一下這兩個概念。git

索引類型

在實際開發中,咱們常常能遇到這樣的場景,在對象中獲取一些屬性的值,而後創建對應的集合。github

let person = {
    name: 'musion',
    age: 35
}

function getValues(person: any, keys: string[]) {
    return keys.map(key => person[key])
}

console.log(getValues(person, ['name', age])) // ['musion', 35]
console.log(getValues(person, ['gender'])) // [undefined]
複製代碼

在上述例子中,能夠看到getValues(persion, ['gender'])打印出來的是[undefined],可是ts編譯器並無給出報錯信息,那麼如何使用ts對這種模式進行類型約束呢?這裏就要用到了索引類型,改造一下getValues函數,經過 索引類型查詢索引訪問 操做符:typescript

function getValues<T, K extends keyof T>(person: T, keys: K[]): T[K][] {
  return keys.map(key => person[key]);
}

interface Person {
    name: string;
    age: number;
}

const person: Person = {
    name: 'musion',
    age: 35
}

getValues(person, ['name']) // ['musion']
getValues(person, ['gender']) // 報錯:
// Argument of Type '"gender"[]' is not assignable to parameter of type '("name" | "age")[]'.
// Type "gender" is not assignable to type "name" | "age".
複製代碼

編譯器會檢查傳入的值是不是Person的一部分。經過介紹幾個概念來理解上面的代碼:數組

1.索引類型查詢操做符(keyof T)

keyof T的含義表示類型T全部公共屬性的字面量的聯合類型,在上述例子中,就是Person的屬性名,即['name', age]bash

2.索引訪問操做符(T[K])

T[K]表示對象T的屬性K所表示的類型,在上述例子中,T[K][] 表示變量T取屬性K的值的數組函數

3.泛型約束(K extends T)

泛型變量能夠經過繼承某些類型獲取某些屬性ui

介紹完這三個概念以後,應該就能夠理解上面的代碼了。首先看泛型,這裏有T和K兩種類型,根據類型推斷,第一個參數person就是person,類型會被推斷爲Person。而第二個數組參數的類型推斷(K extends keyof T),keyof關鍵字能夠獲取T,也就是Person的全部屬性名,即['name', 'age']。而extends關鍵字讓泛型K繼承了Person的全部屬性名,即['name', 'age']。這三個特性組合保證了代碼的動態性和準確性,也讓代碼提示變得更加豐富了:es5

getValues(person, ['gender']) // 報錯:
// Argument of Type '"gender"[]' is not assignable to parameter of type '("name" | "age")[]'.
// Type "gender" is not assignable to type "name" | "age".
複製代碼

映射類型

一個常見的任務是將一個已知的類型每一個屬性都變爲可選的:spa

interface PersonPartial {
    name?: string;
    age?: number;
}
複製代碼

在實例化Person時,咱們沒必要給每一個屬性都賦值,想要一個只讀版本:

interface PersonReadonly {
    readonly name: string;
    readonly age: number;
}
複製代碼

這在JavaScript裏常常出現,TypeScript提供了從舊類型中建立新類型的一種方式 — 映射類型。 在映射類型裏,新類型以相同的形式去轉換舊類型裏每一個屬性。TS內置了一些映射類型(其實是一些語法糖),讓咱們能夠方便地進行類型映射,能夠在TypeScript包中的typescript/lib/lib.es5.d.ts中找到他們的定義: 舉一些例子:

// 將傳入的屬性變爲只讀選項
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
}
// 將傳入的屬性變爲可選項:keyof T 拿到 T 全部屬性名, 而後 in 進行遍歷, 將值賦給 P, 最後 T[P] 取得相應屬性的值.
type Partial<T> = {
    [P in keyof T]?: T[P];
}
複製代碼

使用方式以下:

type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;
複製代碼

TypeScript內置了Readonly和Partial,因此不須要手動聲明實現。除此以外,還有其餘經常使用的內置類型如:

Record

將K中的全部屬性的值轉化爲T類型:

type Record<K extends keyof any, T> = { [P in K]: T };
複製代碼

Pick

從 T 中取出 一系列 K 的屬性

type Pick<T, K extends keyof T> = { [P in K]: T[P] };
複製代碼

Required

將傳入的屬性變爲必選項

type Required<T> = { [P in keyof T]-?: T[P] };
複製代碼

參考連接

Typescript高級類型

相關文章
相關標籤/搜索