文章首發於: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的一部分。經過介紹幾個概念來理解上面的代碼:數組
keyof T的含義表示類型T全部公共屬性的字面量的聯合類型,在上述例子中,就是Person的屬性名,即['name', age]bash
T[K]表示對象T的屬性K所表示的類型,在上述例子中,T[K][] 表示變量T取屬性K的值的數組函數
泛型變量能夠經過繼承某些類型獲取某些屬性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,因此不須要手動聲明實現。除此以外,還有其餘經常使用的內置類型如:
將K中的全部屬性的值轉化爲T類型:
type Record<K extends keyof any, T> = { [P in K]: T };
複製代碼
從 T 中取出 一系列 K 的屬性
type Pick<T, K extends keyof T> = { [P in K]: T[P] };
複製代碼
將傳入的屬性變爲必選項
type Required<T> = { [P in keyof T]-?: T[P] };
複製代碼