<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
TypeScript 允許我們遍歷某種型別的屬性,並通過 keyof 操作符提取其屬性的名稱。keyof 操作符是在 TypeScript 2.1 版本引入的,該操作符可以用於獲取某種型別的所有鍵,其返回型別是聯合型別。
下面我們來看個例子:
interface Person { name: string; age: number; location: string; } type K1 = keyof Person; // "name" | "age" | "location" type K2 = keyof Person[]; // number | "length" | "push" | "concat" | ... type K3 = keyof { [x: string]: Person }; // string | number
除了介面外,keyof 也可以用於操作類,比如:
class Person { name: string = "Semlinker"; } let sname: keyof Person; sname = "name";
若把 sname = “name” 改為 sname = “age” 的話,TypeScript 編譯器會提示以下錯誤資訊:
Type '"age"' is not assignable to type '"name"'.
keyof 操作符除了支援介面和類之外,它也支援基本資料型別:
let K1: keyof boolean; // let K1: "valueOf"
let K2: keyof number; // let K2: "toString" | "toFixed" | "toExponential" | ...
let K3: keyof symbol; // let K1: "valueOf"
此外 keyof 也稱為輸入索引型別查詢,與之相對應的是索引存取型別,也稱為查詢型別。在語法上,它們看起來像屬性或元素存取,但最終會被轉換為型別:
type P1 = Person["name"]; // string type P2 = Person["name" | "age"]; // string | number type P3 = string["charAt"]; // (pos: number) => string type P4 = string[]["push"]; // (...items: string[]) => number type P5 = string[][0]; // string
JavaScript 是一種高度動態的語言。有時在靜態型別系統中捕獲某些操作的語意可能會很棘手。以一個簡單的prop 函數為例:
function prop(obj, key) { return obj[key]; }
該函數接收 obj 和 key 兩個引數,並返回對應屬性的值。物件上的不同屬性,可以具有完全不同的型別,我們甚至不知道 obj 物件長什麼樣。
那麼在 TypeScript 中如何定義上面的 prop 函數呢?我們來嘗試一下:
function prop(obj: object, key: string) { return obj[key]; }
在上面程式碼中,為了避免呼叫 prop 函數時傳入錯誤的引數型別,我們為 obj 和 key 引數設定了型別,分別為 {} 和 string 型別。然而,事情並沒有那麼簡單。針對上述的程式碼,TypeScript 編譯器會輸出以下錯誤資訊:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'
元素隱式地擁有 any 型別,因為 string 型別不能被用於索引 {} 型別。要解決這個問題,你可以使用以下非常暴力的方案:
function prop(obj: object, key: string) { return (obj as any)[key]; }
很明顯該方案並不是一個好的方案,我們來回顧一下 prop 函數的作用,該函數用於獲取某個物件中指定屬性的屬性值。因此我們期望使用者輸入的屬性是物件上已存在的屬性,那麼如何限制屬性名的範圍呢?這時我們可以利用本文的主角 keyof 操作符:
function prop<T extends object, K extends keyof T>(obj: T, key: K) { return obj[key]; }
在以上程式碼中,我們使用了 TypeScript 的泛型和泛型約束。首先定義了 T 型別並使用 extends 關鍵字約束該型別必須是 object 型別的子型別,然後使用 keyof 操作符獲取 T 型別的所有鍵,其返回型別是聯合型別,最後利用 extends 關鍵字約束 K 型別必須為 keyof T 聯合型別的子型別。 是騾子是馬拉出來遛遛就知道了,我們來實際測試一下:
type Todo = { id: number; text: string; done: boolean; } const todo: Todo = { id: 1, text: "Learn TypeScript keyof", done: false } function prop<T extends object, K extends keyof T>(obj: T, key: K) { return obj[key]; } const id = prop(todo, "id"); // const id: number const text = prop(todo, "text"); // const text: string const done = prop(todo, "done"); // const done: boolean
很明顯使用泛型,重新定義後的 prop<T extends object, K extends keyof T>(obj: T, key: K) 函數,已經可以正確地推匯出指定鍵對應的型別。那麼當存取 todo 物件上不存在的屬性時,會出現什麼情況?比如:
const date = prop(todo, "date");
對於上述程式碼,TypeScript 編譯器會提示以下錯誤:
Argument of type '"date"' is not assignable to parameter of type '"id" | "text" | "done"'.
這就阻止我們嘗試讀取不存在的屬性。
在使用物件的數值屬性時,我們也可以使用 keyof 關鍵字。請記住,如果我們定義一個帶有數值屬性的物件,那麼我們既需要定義該屬性,又需要使用陣列語法存取該屬性, 如下所示:
class ClassWithNumericProperty { [1]: string = "Semlinker"; } let classWithNumeric = new ClassWithNumericProperty(); console.log(`${classWithNumeric[1]} `);
下面我們來舉個範例,介紹一下在含有數值屬性的物件中,如何使用 keyof 操作符來安全地存取物件的屬性:
enum Currency { CNY = 6, EUR = 8, USD = 10 } const CurrencyName = { [Currency.CNY]: "人民幣", [Currency.EUR]: "歐元", [Currency.USD]: "美元" }; console.log(`CurrencyName[Currency.CNY] = ${CurrencyName[Currency.CNY]}`); console.log(`CurrencyName[36] = ${CurrencyName[6]}`);
上面的程式碼中,首先定義了一個 Currency 列舉用於表示三種貨幣型別,接著定義一個 CurrencyName 物件,該物件使用數值屬性作為鍵,對應的值是該貨幣型別的名稱。該程式碼成功執行後,控制檯會輸出以下結果:
CurrencyName[Currency.CNY] = 人民幣 CurrencyName[36] = 人民幣
為了方便使用者能根據貨幣型別來獲取對應的貨幣名稱,我們來定義一個 getCurrencyName 函數,具體實現如下:
function getCurrencyName<T, K extends keyof T>(key: K, map: T): T[K] { return map[key]; } console.log(`name = ${getCurrencyName(Currency.CNY, CurrencyName)}`);
同樣,getCurrencyName 函數和前面介紹的 prop 函數一樣,使用了泛型和泛型約束,從而來保證屬性的安全存取。最後,我們來簡單介紹一下 keyof 與 typeof 操作符如何配合使用。
typeof 操作符用於獲取變數的型別。因此這個操作符的後面接的始終是一個變數,且需要運用到型別定義當中。為了方便大家理解,我們來舉一個具體的範例:
type Person = { name: string; age: number; } let man: Person = { name: "Semlinker", age: 30 } type Human = typeof man;
瞭解完 typeof 和 keyof 操作符的作用,我們來舉個例子,介紹一下它們如何結合在一起使用:
const COLORS = { red: 'red', blue: 'blue' } // 首先通過typeof操作符獲取color變數的型別,然後通過keyof操作符獲取該型別的所有鍵, // 即字串字面量聯合型別 'red' | 'blue' type Colors = keyof typeof COLORS let color: Colors; color = 'red' // Ok color = 'blue' // Ok // Type '"yellow"' is not assignable to type '"red" | "blue"'. color = 'yellow' // Error
最後留到思考題,有興趣的小夥伴可以想一想:
interface StringIndexArray { [index: string]: string; } interface NumberIndexArray { [index: number]: string; } type K1 = keyof StringIndexArray // type K1 = string | number type K2 = keyof NumberIndexArray // type K2 = number
到此這篇關於淺談typescript中keyof與typeof操作符用法的文章就介紹到這了,更多相關typescript中keyof與typeof操作符內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45