TypeScript中的類型運(yùn)算符實現(xiàn)
1. keyof運(yùn)算符
1. 簡介
是一個單目運(yùn)算符,接受一個對象類型作為參數(shù),返回該對象的所有鍵名組成的聯(lián)合類型。
type MyObj = { foo: number, bar: string, }; type Keys = keyof MyObj; // 'foo'|'bar'
這個例子keyof MyObj
返回MyObj
的所有鍵名組成的聯(lián)合類型,即'foo'|'bar'
由于 JavaScript 對象的鍵名只有三種類型,所以對于任意對象的鍵名的聯(lián)合類型就是string|number|symbol
。
對于沒有自定義鍵名的類型使用 keyof 運(yùn)算符,返回never
類型,表示不可能有這樣類型的鍵名
type KeyT = keyof object; // never
上面示例中,由于object
類型沒有自身的屬性,也就沒有鍵名,所以keyof object
返回never
類型。
由于 keyof 返回的類型是string|number|symbol
,如果有些場合只需要其中的一種類型,那么可以采用交叉類型的寫法。
type Capital<T extends string> = Capitalize<T>; type MyKeys<Obj extends object> = Capital<keyof Obj>; // 報錯 type MyKeys<Obj extends object> = Capital<string & keyof Obj>;
這個列子中,string & keyof Obj
等同于string & string|number|symbol
進(jìn)行交集運(yùn)算,最后返回string
,因此Capital<T extends string>
就不會報錯了。
如果對象屬性名采用索引形式,keyof 會返回屬性名的索引類型。
// 示例一 interface T { [prop: number]: number; } // number type KeyT = keyof T; // 示例二 interface T { [prop: string]: number; } // string|number type KeyT = keyof T;
上面的示例二,keyof T
返回的類型是string|number
,原因是 JavaScript 屬性名為字符串時,包含了屬性名為數(shù)值的情況,因為數(shù)值屬性名會自動轉(zhuǎn)為字符串
如果 keyof 運(yùn)算符用于數(shù)組或元組類型,得到的結(jié)果可能出人意料。
type Result = keyof ['a', 'b', 'c']; // 返回 number | "0" | "1" | "2" // | "length" | "pop" | "push" | ···
上面示例中,keyof 會返回數(shù)組的所有鍵名,包括數(shù)字鍵名和繼承的鍵名。
對于聯(lián)合類型,keyof 返回成員共有的鍵名。
type A = { a: string; z: boolean }; type B = { b: string; z: boolean }; // 返回 'z' type KeyT = keyof (A | B);
對于交叉類型,keyof 返回所有鍵名。
type A = { a: string; x: boolean }; type B = { b: string; y: number }; // 返回 'a' | 'x' | 'b' | 'y' type KeyT = keyof (A & B); // 相當(dāng)于 keyof (A & B) ≡ keyof A | keyof B
keyof 取出的是鍵名組成的聯(lián)合類型,如果想取出鍵值組成的聯(lián)合類型,可以像下面這樣寫。
type MyObj = { foo: number, bar: string, }; type Keys = keyof MyObj; type Values = MyObj[Keys]; // number|string
上面示例中,Keys
是鍵名組成的聯(lián)合類型,而MyObj[Keys]
會取出每個鍵名對應(yīng)的鍵值類型,組成一個新的聯(lián)合類型,即number|string
。
2. keyof運(yùn)算符的用途
- 往往用于精確表達(dá)對象的屬性類型
- 用于屬性映射,即將一個類型的所有屬性逐一映射成其他值
2. in運(yùn)算符
在js中in用來確定對象是否包含某個屬性名,在ts 類型運(yùn)算中,in
運(yùn)算符用來取出(遍歷)聯(lián)合類型的每一個成員類型。
type U = 'a'|'b'|'c'; type Foo = { [Prop in U]: number; }; // 等同于 type Foo = { a: number, b: number, c: number };
[Prop in U]
表示依次取出聯(lián)合類型U
的每一個成員。
3. 方括號運(yùn)算符
用來取出對象的鍵值類型,比如T[K]會返回對象T的屬性K的類型。
type Person = { age: number; name: string; alive: boolean; }; // Age 的類型是 number type Age = Person['age'];
方括號的參數(shù)如果是聯(lián)合類型,那么返回的也是聯(lián)合類型。
type Person = { age: number; name: string; alive: boolean; }; // number|string type T = Person['age'|'name']; // number|string|boolean type A = Person[keyof Person];
如果訪問不存在的屬性,會報錯。
type T = Person['notExisted']; // 報錯
方括號運(yùn)算符的參數(shù)也可以是屬性名的索引類型。
type Obj = { [key:string]: number, }; // number type T = Obj[string];
這個語法對于數(shù)組也適用,可以使用number
作為方括號的參數(shù)。
// MyArray 的類型是 { [key:number]: string } const MyArray = ['a','b','c']; // 等同于 (typeof MyArray)[number] // 返回 string type Person = typeof MyArray[number];
上面示例中,MyArray
是一個數(shù)組,它的類型實際上是屬性名的數(shù)值索引,而typeof MyArray[number]
的typeof
運(yùn)算優(yōu)先級高于方括號,所以返回的是所有數(shù)值鍵名的鍵值類型string
。
方括號里面不能有值的運(yùn)算。
// 示例一 const key = 'age'; type Age = Person[key]; // 報錯 // 示例二 type Age = Person['a' + 'g' + 'e']; // 報錯
上面兩個示例,方括號里面都涉及值的運(yùn)算,編譯時不會進(jìn)行這種運(yùn)算,所以會報錯。
4. extends…?:條件運(yùn)算符
可以根據(jù)當(dāng)前類型是否符合某種條件,返回不同的類型。
T extends U ? X : Y
上面式子中的extends
用來判斷,類型T
是否可以賦值給類型U
,即T
是否為U
的子類型,這里的T
和U
可以是任意類型。如果T
能夠賦值給類型U
,表達(dá)式的結(jié)果為類型X
,否則結(jié)果為類型Y
。
// true type T = 1 extends number ? true : false;
上面示例中,1
是number
的子類型,所以返回true
。
如果需要判斷的類型是一個聯(lián)合類型,那么條件運(yùn)算符會展開這個聯(lián)合類型。
(A|B) extends U ? X : Y // 等同于 (A extends U ? X : Y) | (B extends U ? X : Y)
上面示例中,A|B
是一個聯(lián)合類型,進(jìn)行條件運(yùn)算時,相當(dāng)于A
和B
分別進(jìn)行運(yùn)算符,返回結(jié)果組成一個聯(lián)合類型。
如果不希望聯(lián)合類型被條件運(yùn)算符展開,可以把extends
兩側(cè)的操作數(shù)都放在方括號里面。
// 示例一 type ToArray<Type> = Type extends any ? Type[] : never; // string[]|number[] type T = ToArray<string|number>; // 示例二 type ToArray<Type> = [Type] extends [any] ? Type[] : never; // (string | number)[] type T = ToArray<string|number>;
上面的示例一,傳入ToArray<Type>
的類型參數(shù)是一個聯(lián)合類型,所以會被展開,返回的也是聯(lián)合類型。示例二是extends
兩側(cè)的運(yùn)算數(shù)都放在方括號里面,所以傳入的聯(lián)合類型不會展開,返回的是一個數(shù)組。
條件運(yùn)算符還可以嵌套使用。
type LiteralTypeName<T> = T extends undefined ? "undefined" : T extends null ? "null" : T extends boolean ? "boolean" : T extends number ? "number" : T extends bigint ? "bigint" : T extends string ? "string" : never; // "bigint" type Result1 = LiteralTypeName<123n>; // "string" | "number" | "boolean" type Result2 = LiteralTypeName<true | 1 | 'a'>;
上面示例是一個多重判斷,返回一個字符串的值類型,對應(yīng)當(dāng)前類型。
5. infer關(guān)鍵字
用來定義泛型里面推斷出來的類型參數(shù),而不是外部傳入的類型參數(shù)。它通常跟條件運(yùn)算符一起使用,用在extends關(guān)鍵字后面的父類型中。
type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;
上面示例中,infer Item
表示Item
這個參數(shù)是 TypeScript 自己推斷出來的,不用顯式傳入,而Flatten<Type>
則表示Type
這個類型參數(shù)是外部傳入的。Type extends Array<infer Item>
則表示,如果參數(shù)Type
是一個數(shù)組,那么就將該數(shù)組的成員類型推斷為Item
,即Item
是從Type
推斷出來的。
一旦使用Infer Item
定義了Item
,后面的代碼就可以直接調(diào)用Item
了。下面是上例的泛型Flatten<Type>
的用法。
// string type Str = Flatten<string[]>; // number type Num = Flatten<number>;
上面示例中,第一個例子Flatten<string[]>
傳入的類型參數(shù)是string[]
,可以推斷出Item
的類型是string
,所以返回的是string
。第二個例子Flatten<number>
傳入的類型參數(shù)是number
,它不是數(shù)組,所以直接返回自身。
如果不用infer
定義類型參數(shù),那么就要傳入兩個類型參數(shù)。
type Flatten<Type, Item> = Type extends Array<Item> ? Item : Type;
上面是不使用infer
的寫法,每次調(diào)用Flatten
的時候,都要傳入兩個參數(shù),就比較麻煩。
下面的例子使用infer
,推斷函數(shù)的參數(shù)類型和返回值類型。
type ReturnPromise<T> = T extends (...args: infer A) => infer R ? (...args: A) => Promise<R> : T;
上面示例中,如果T
是函數(shù),就返回這個函數(shù)的 Promise 版本,否則原樣返回。infer A
表示該函數(shù)的參數(shù)類型為A
,infer R
表示該函數(shù)的返回值類型為R
。
如果不使用infer
,就不得不把ReturnPromise<T>
寫成ReturnPromise<T, A, R>
,這樣就很麻煩,相當(dāng)于開發(fā)者必須人肉推斷編譯器可以完成的工作。
下面是infer
提取對象指定屬性的例子。
type MyType<T> = T extends { a: infer M, b: infer N } ? [M, N] : never; // 用法示例 type T = MyType<{ a: string; b: number }>; // [string, number]
上面示例中,infer
提取了參數(shù)對象的屬性a
和屬性b
的類型。
下面是infer
通過正則匹配提取類型參數(shù)的例子。
type Str = 'foo-bar'; type Bar = Str extends `foo-${infer rest}` ? rest : never // 'bar'
上面示例中,rest
是從模板字符串提取的類型參數(shù)。
6. is運(yùn)算符
函數(shù)返回布爾值時,可以使用is運(yùn)算符,來限定返回值與參數(shù)之間的關(guān)系。
is運(yùn)算符用來描述返回值是true還是false。
function isFish( pet: Fish|Bird ):pet is Fish { return (pet as Fish).swim !== undefined; }
上面示例中,函數(shù)isFish()
的返回值類型為pet is Fish
,表示如果參數(shù)pet
類型為Fish
,則返回true
,否則返回false
。
is
運(yùn)算符總是用于描述函數(shù)的返回值類型,寫法采用parameterName is Type
的形式,即左側(cè)為當(dāng)前函數(shù)的參數(shù)名,右側(cè)為某一種類型。它返回一個布爾值,表示左側(cè)參數(shù)是否屬于右側(cè)的類型。
is
運(yùn)算符可以用于類型保護(hù)。
function isCat(a:any): a is Cat { return a.name === 'kitty'; } let x:Cat|Dog; if (isCat(x)) { x.meow(); // 正確,因為 x 肯定是 Cat 類型 }
上面示例中,函數(shù)isCat()
的返回類型是a is Cat
,它是一個布爾值。后面的if
語句就用這個返回值進(jìn)行判斷,從而起到類型保護(hù)的作用,確保x
是 Cat 類型,從而x.meow()
不會報錯(假定Cat
類型擁有meow()
方法)
is
運(yùn)算符還有一種特殊用法,就是用在類(class)的內(nèi)部,描述類的方法的返回值。
class Teacher { isStudent():this is Student { return false; } } class Student { isStudent():this is Student { return true; } }
上面示例中,isStudent()
方法的返回值類型,取決于該方法內(nèi)部的this
是否為Student
對象。如果是的,就返回布爾值true
,否則返回false
。
注意,this is T
這種寫法,只能用來描述方法的返回值類型,而不能用來描述屬性的類型。
7. 模板字符串
ts可以使用模板字符串構(gòu)建類型,模板字符串最大的特點(diǎn)就是內(nèi)部可以引用其他類型。
type World = "world"; // "hello world" type Greeting = `hello ${World}`;
上面示例中,類型Greeting
是一個模板字符串,里面引用了另一個字符串類型world
,因此Greeting
實際上是字符串hello world
模板字符串可以引用的類型一共6種,分別是 string、number、bigint、boolean、null、undefined。引用這6種以外的類型會報錯。
模板字符串里面引用的類型,如果是一個聯(lián)合類型,那么它返回的也是一個聯(lián)合類型,即模板字符串可以展開聯(lián)合類型。
type T = 'A'|'B'; // "A_id"|"B_id" type U = `${T}_id`;
上面示例中,類型U
是一個模板字符串,里面引用了一個聯(lián)合類型T
,導(dǎo)致最后得到的也是一個聯(lián)合類型。
如果模板字符串引用兩個聯(lián)合類型,它會交叉展開這兩個類型。
type T = 'A'|'B'; type U = '1'|'2'; // 'A1'|'A2'|'B1'|'B2' type V = `${T}${U}`;
上面示例中,T
和U
都是聯(lián)合類型,各自有兩個成員,模板字符串里面引用了這兩個類型,最后得到的就是一個4個成員的聯(lián)合類型。
8. satisfies運(yùn)算符
satisfies 是 TypeScript 4.9 版本中引入的一個新的運(yùn)算符,它可以讓你檢查一個給定的類型是否滿足一個特定的接口或條件。換句話說,它可以確保一個類型具有一個特定接口所要求的所有屬性和方法。它是一種保證一個變量符合一個類型定義的方式。
satisfies 運(yùn)算符的語法是在一個值后面加上 satisfies,然后跟上一個類型的名稱:
someValue satisfies SomeType;
satisfies 運(yùn)算符有以下優(yōu)點(diǎn):
- 它可以讓你在不改變值的原始類型的情況下,對值的類型進(jìn)行驗證和約束(與 : 注解不同)。
- 它可以讓你保留值的最具體的類型信息,而不是將其擴(kuò)展為更一般的類型(與默認(rèn)類型推斷不同)。
舉個例子,假設(shè)我們有一個 Vibe 接口,它定義了一個 mood 屬性,其類型為 "happy" | "sad"。我們可以用 satisfies 運(yùn)算符來保證我們創(chuàng)建的 vibe 對象的 mood 屬性只能是這兩個字符串字面量之一,同時還能保持 mood 屬性的具體值為 "happy"。
interface Vibe { mood: "happy" | "sad"; } const vibe = { mood: "happy" } satisfies Vibe; vibe.mood; // "happy"
到此這篇關(guān)于TypeScript中的類型運(yùn)算符實現(xiàn)的文章就介紹到這了,更多相關(guān)TypeScrip 類型運(yùn)算符內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript使用addEventListener添加事件監(jiān)聽用法實例
這篇文章主要介紹了JavaScript使用addEventListener添加事件監(jiān)聽的方法,實例分析了addEventListener方法的相關(guān)使用技巧,需要的朋友可以參考下2015-06-06js定時器(執(zhí)行一次、重復(fù)執(zhí)行)
這篇文章主要分享一段js代碼,有關(guān)js定時器的小例子,分為執(zhí)行一次的定時器與重復(fù)執(zhí)行的定時器,需要的朋友可以參考下2014-03-03JS生態(tài)系統(tǒng)加速模塊解析賦能性能優(yōu)化探索
這篇文章主要為大家介紹了JS生態(tài)系統(tǒng)加速模塊解析賦能性能優(yōu)化探索,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01微信小程序canvas.drawImage完全顯示圖片問題的解決
這篇文章主要介紹了微信小程序canvas.drawImage完全顯示圖片問題的解決,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-11-11javascript得到當(dāng)前頁的來路即前一頁地址的方法
這篇文章主要介紹了javascript得到當(dāng)前頁的來路即前一頁地址的方法,需要的朋友可以參考下2014-02-02js?通過Object.defineProperty()?定義和控制對象屬性
這篇文章主要介紹了js?通過Object.defineProperty()?定義和控制對象屬性,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下2022-08-08