TS 內置工具類型中的 keyof 操作符有啥用?
你用過上圖中 Partial、Required、Record 和 Pick 這些工具類型么?在這些工具類型內部都使用了 keyof 操作符,那么該操作符的作用是什么?如果不清楚的話,閱讀完本期的內容,也許你就懂了。
在 JavaScript 中,我們可以通過 Object.keys 方法來獲取對象中的鍵,返回的是鍵組成的數(shù)組。
const user = {
id: 666,
name: "阿寶哥",
}
const keys = Object.keys(user); // ["id", "name"]
而在 TypeScript 中,我們面對的是類型。如果要獲取對象類型中的鍵,就需要使用 keyof 操作符。該操作符是在 TypeScript 2.1 版本中引入的,用于獲取某種類型中的所有鍵,其返回類型是聯(lián)合類型。
type User = {
id: number;
name: string;
}
type UserKeys = keyof User; // "id" | "name"
在獲取對象類型的鍵之后,我們就可以通過類似屬性訪問的語法來訪問該鍵對應的值的類型。
type U1 = User["id"] // number
type U2 = User["id" | "name"] // string | number
type U3 = User[keyof User] // string | number
那么在實際工作中,keyof 操作符有什么用呢?這里我們來舉一個例子。
這是一個簡單的 getProperty 函數(shù),它接收 obj 和 key 兩個參數(shù),用于獲取 obj 對象上 key 參數(shù)對應的屬性值。
function getProperty(obj, key) {
return obj[key];
}
const user = {
id: 666,
name: "阿寶哥",
}
const userName = getProperty(user, "name");
那么在 TS 中如何定義上述的 getProperty 函數(shù)呢?這里我們直接把該函數(shù)復制到 TS 項目中。對于上述的代碼,TS 編譯器會提示以下錯誤信息:
參數(shù)“obj”隱式具有“any”類型。ts(7006)
參數(shù)“key”隱式具有“any”類型。ts(7006)
該信息告訴我們 obj 和 key 參數(shù)隱式具有 "any" 類型。要解決該問題,我們可以顯式定義 obj 和 key 參數(shù)的類型。
function getProperty(obj: object, key: string) {
return obj[key]; // Error
}
設置之后參數(shù)上的錯誤消息消失了,但函數(shù)體中又出現(xiàn)了新的錯誤信息:
元素隱式具有 "any" 類型,因為類型為 "string" 的表達式不能用于索引類型 "{}"。
在類型 "{}" 上找不到具有類型為 "string" 的參數(shù)的索引簽名。ts(7053)
那么又該如何解決上述問題呢?這時我們可以使用 TS 泛型和本期的主角 keyof 操作符:
function getProperty<T extends object, K extends keyof T>(
obj: T, key: K
) {
return obj[key];
}
在以上代碼中,我們定義了兩個類型變量 T 和 K。對于類型變量 T 使用 extends 約束該類型變量對應的實際類型必須是 object 類型的子類型。而類型變量 K 也使用 extends 約束該類型變量對應的實際類型為對象類型所有鍵組成的聯(lián)合類型的子類型。
之后,利用 getProperty 函數(shù),我們就可以獲取指定屬性的值。當 key 對應的屬性不存在時,TS 將會提示相應的錯誤。
const userId = getProperty(user, "id"); // Ok
const userName = getProperty(user, "name"); // Ok
const userAge = getProperty(user, "age"); // Error
keyof 操作符不僅可以應用于對象類型,也可以應用在基本數(shù)據(jù)類型、any 類型、類和枚舉類型上。
type K1 = keyof boolean; // "valueOf"
type K2 = keyof number; // "toString" | "toFixed" | "toExponential" | "toPrecision" | "valueOf" | "toLocaleString"
type K3 = keyof any; // string | number | symbol
class Person {
id: number = 666;
name: string = "阿寶哥";
}
type P = keyof Person; // "id" | "name"
enum HttpMethod {
Get,
Post,
}
type Method = keyof typeof HttpMethod; // "Get" | "Post"
keyof 操作符的另一個常見用途是映射類型,關于映射類型的相關知識點,阿寶哥將在后面的文章中單獨介紹。
閱讀完本文之后,你應該就知道 TS 內置工具類型中 keyof 操作符的作用了。