typescript?type類(lèi)型使用梳理總結(jié)
引言
倉(cāng)庫(kù)地址:https://github.com/goblin-pitcher/steel-wheel-run/blob/master/typescript/type-challenge-pre-content.md
最近準(zhǔn)備開(kāi)始刷type-challenges,因此先梳理一下ts類(lèi)型相關(guān)知識(shí)點(diǎn)。
遺漏知識(shí)點(diǎn)總結(jié)
ts
的type符合圖靈完備。這意味著ts
類(lèi)型包含循環(huán)、判斷等一系列基本操作。
類(lèi)型集合
類(lèi)型應(yīng)當(dāng)作集合來(lái)看,其中:
unknown
是全集,包含所有類(lèi)型,是所有類(lèi)型的父級(jí)
之前在泛型中會(huì)寫(xiě)Comp<T extends unknown>
看來(lái)是有些多此一舉了...
never
是空集,是所有類(lèi)型的子集
ts的設(shè)計(jì)符合里氏替換原則,即子集可以替換所有父級(jí),這意味著:
interface Parent { id: string; } // 符合里氏替換原則,不會(huì)報(bào)錯(cuò) interface Child extends Parent { id: "childIdA"|"childIdB"; // "childIdA"|"childIdB"是string的子集 key: number; // Parent中沒(méi)有key,該屬性是Parent的拓展 } // 不符合里氏替換原則,報(bào)錯(cuò) interface ChildError extends Parent { id: number; // number不是string的子集 } type A = {a: string}; type B = {a: "A"|"a", b: number}; let a: A = {a: "xxx"}; let b: B = {a: "a", b: 2}; // 符合B是A的子集,該賦值符合里氏替換原則, 反之 b=a 會(huì)報(bào)錯(cuò) a = b;
將TS的type看作編程語(yǔ)言
既然ts
的type是圖靈完備的,那么它自然可以完成一切計(jì)算,因此可以看作是一門(mén)語(yǔ)言
函數(shù)
在類(lèi)型語(yǔ)境中,可將泛型看做是函數(shù)
type Fn<T> = T; // 看作 const Fn = val => val;
循環(huán)
經(jīng)??梢栽诖a中看到如下寫(xiě)法:
type R = "A" | "B" type T = { [k in R]: k }
這里[k in R]
可看做循環(huán)for(const k in R){}
借助此特性,可以完成如下操作:
type MyPick<T, K extends keyof T> = { [k in K]: T[k] } type A = MyPick<{a: string, b: number, c: boolean}, "a"|"c">
當(dāng)然這種循環(huán)只針對(duì)特定類(lèi)型("a"|"b"
這種),稍微復(fù)雜點(diǎn)的循環(huán)還是得通過(guò)遞歸實(shí)現(xiàn),例子的話(huà),后面用ts type
實(shí)現(xiàn)四則運(yùn)算時(shí)會(huì)用到。
條件語(yǔ)句
type
支持三元運(yùn)算符,這個(gè)沒(méi)什么好說(shuō)的。。
需要注意的是,type
里沒(méi)有等號(hào),但可以用extends代替等號(hào)
type Equal5<T> = T extends 5 ? true : false;
來(lái)點(diǎn)練習(xí)
// eq1: 實(shí)現(xiàn)`GetProp<Obj, K>`(獲取Obj[K]類(lèi)型) type GetProp<Obj, K> = K extends keyof Obj ? Obj[K] : undefined; // eq2: 實(shí)現(xiàn)getName<User> type GetName<User> = GetProp<User, "name">
extends
也常和infer
一起用于類(lèi)型推斷。
例如
// 實(shí)現(xiàn)KeyOf type KeyOf<T> = T extends {[k in infer U]: unknown} ? U : never; // 實(shí)現(xiàn)ValueOf type ValueOf<T> = T extends { [k in keyof T]: infer U } ? U : never; // 實(shí)現(xiàn)Parameters和ReturnType type FuncBase<F, C extends "params"|"return"> = F extends (...params: infer P) => infer R ? (C extends "params" ? P : R) : never; type Params<F> = FuncBase<F, "params">; type Return<F> = FuncBase<F, "return">;
賦值
賦值部分參看前面類(lèi)型集合章節(jié),賦值要遵循里氏替換原則。
條件語(yǔ)句章節(jié)提到了infer
,或許可以用infer
實(shí)現(xiàn)解構(gòu)賦值。
// 需要實(shí)現(xiàn)類(lèi)似js的const {name, ...extra} = user,求extra。(其實(shí)就是Omit方法) type MyPick<Obj, T extends keyof Obj> = {[k in T]: Obj[k]}; type MyExclude<Obj, T extends keyof Obj> = keyof Obj extends T|(infer U extends keyof Obj) ? U : never; type MyOmit<Obj, T extends keyof Obj> = MyPick<Obj, MyExclude<Obj, T>>; // 實(shí)現(xiàn)數(shù)組的解構(gòu)賦值const [A, ...extra] = arr; type GetArrBase<T extends unknown[], C extends "first"|"rest"> = T extends [infer First, ...infer Rest] ? (C extends "first"?First: Rest): never; type GetFirst<T extends unknown[]> = GetArrBase<T, "first">; type GetRest<T extends unknown[]> = GetArrBase<T, "rest">;
對(duì)象
type
的對(duì)象可以類(lèi)比js
中的對(duì)象,使用方法如下,注意最后一個(gè)例子
type Obj = { name: string; age: 20; } Obj["name"] // string; Obj.age // Error Obj["age"] // 20 Obj['name'|'age'] // string | 20 , 這個(gè)特性很重要?。?!
利用這個(gè)特性,可以完成如下功能
interface Test { a: string; b: number; c: boolean; } // 之前不知道這個(gè)特性時(shí),用infer也能達(dá)到同樣的效果,但實(shí)現(xiàn)不如這個(gè)直觀 type ValueOf<T> = T[keyof T]; type R = ValueOf<Test>;
數(shù)組
ts
中的數(shù)組分為Array
數(shù)組和Tuple
元組。
Array
數(shù)組是諸如string[]
的寫(xiě)法,類(lèi)似java
或其他語(yǔ)言的數(shù)組。
Tuple
元組更像是js
中的數(shù)組,寫(xiě)法是[string, number, boolean]
這種。
(注:js
中不存在真正意義上的數(shù)組,數(shù)組是在內(nèi)存上開(kāi)辟連續(xù)空間,每個(gè)單元格所占內(nèi)存都一樣,在js
中,數(shù)組寫(xiě)成['a', 5555, {a: 1}]
都沒(méi)問(wèn)題,顯然在實(shí)現(xiàn)上不是真正的開(kāi)辟了連續(xù)內(nèi)存空間,應(yīng)該是用鏈表模擬的,為了解決鏈表本身查詢(xún)慢的問(wèn)題,應(yīng)該是采用了跳表或者紅黑樹(shù)的方式組織的?)
數(shù)組中需要注意的點(diǎn)如下:
type A = string[]; type B = [string, number, boolean]; // =========================分割線(xiàn)========================== // 重點(diǎn)注意?。?! A[0]; // string A[1]; // string B[0]; // string B[1]; // number; B[0|2]; // string|boolean // 注意以下寫(xiě)法,為什么可以這么寫(xiě),因?yàn)閚umber是所有數(shù)字的集合 A[number]; // string B[number]; // string | number | boolean A["length"]; // number B["length"]; // 3 // ts數(shù)組同樣可以像js數(shù)組那樣展開(kāi) type Spread<T extends unknown[]> = [...T] Spread<[1,2,3]> // [1,2,3]
根據(jù)以上特性,很容易實(shí)現(xiàn)以下練習(xí):
// eq1: 實(shí)現(xiàn) `ItemOf`方法(獲取數(shù)組中項(xiàng)的類(lèi)型) type ItemOf<T extends unknown[]> = T[number]; // 之前不知道這個(gè)特性時(shí),用infer實(shí)現(xiàn)的代碼如下 type ItemOfByinfer<T> = T extends (infer N)[] ? N : never; // eq2:實(shí)現(xiàn)`Append`方法 type Append<T extends unknown[], Ele> = [...T, Ele]; // eq3: 實(shí)現(xiàn)返回?cái)?shù)組length+1 // ts雖然無(wú)法實(shí)現(xiàn)加減運(yùn)算,但可以通過(guò)模擬生成對(duì)應(yīng)新類(lèi)型,返回其屬性,從而模擬加減運(yùn)算 type LengthAddOne<T extends unknown[]> = [unknown, ...T]["length"];
四則運(yùn)算
運(yùn)算加減依賴(lài)于元組長(zhǎng)度,因此先定義一些基本方法,注意..由于是依賴(lài)元組長(zhǎng)度,因此無(wú)法算負(fù)數(shù)和小數(shù),只能算正整數(shù)...
(注:雖然無(wú)法計(jì)算負(fù)數(shù)和小數(shù),但ts的type依舊是圖靈完備的,位運(yùn)算也只是01的運(yùn)算,負(fù)數(shù)和小數(shù)都是一堆01的定義,比如把10000看做0,且最后兩位是小數(shù),那么9999就是 -0.01)
// 返回Push后的數(shù)組 type Append<T extends unknown[], E = unknown> = [...T, U]; // 同理,返回Pop后的數(shù)組代碼如下,暫時(shí)用不到 // type RemoveTop<T extends unknown[]> = T extends [...(infer U), unknown] ? U : never; type Tuple<N extends number, Arr extends unknown[] = []> = Arr["length"] extends N ? Arr : Tuple<N, Append<Arr>>
有了這些基本方法,先實(shí)現(xiàn)加法和減法
type Add<A extends number, B extends number> = [...Tuple<A>, ...Tuple<B>]["length"]; type Subtract<A extends number, B extends number> = Tuple<A> extends [...Tuple<B>, ...(infer U)] ? U["length"] : never;
乘法的話(huà),A*B
就是A
個(gè)B
相加,簡(jiǎn)易版乘法如下,思路不難,但直接用Add和Subtract封裝,很多寫(xiě)法都提示嵌套太深。。
注意,這里用于統(tǒng)計(jì)和的參數(shù)S以元組表示,因?yàn)樗羞\(yùn)算都是以元組為基準(zhǔn),S用數(shù)字表示會(huì)先轉(zhuǎn)元組再轉(zhuǎn)數(shù)字,來(lái)來(lái)回回開(kāi)銷(xiāo)比較大。
type MultipleBase<A extends number, B extends number, S extends unknown[] = []> = B extends 0 ? S["length"] : MultipleBase<A, Subtract<B, 1>, [...S, ...Tuple<A>]>;
乘法還有優(yōu)化的空間,例如2*100
,直接用這個(gè)算的是100個(gè)2相加,時(shí)間復(fù)雜度不如100*2
,而計(jì)算這么優(yōu)化的前提是,實(shí)現(xiàn)BiggerThan
方法。
type BiggerThan<A extends number, B extends number> = Tuple<A> extends [...Tuple<B>, ...infer U] ? (U["length"] extends (never|0) ? false : true): false; // 優(yōu)化后的乘法如下 type Mutiple<A extends number, B extends number> = BiggerThan<A, B> extends true ? MultipleBase<A, B> : MultipleBase<B, A>;
有了BiggerThan
,除法也好說(shuō),例如a/b
,判定b*2、b*3...b*n
和A的大小就行。
同乘法的實(shí)現(xiàn),用于統(tǒng)計(jì)的參數(shù)R為元組。。
type Divide<A extends number, B extends number, R extends unknown[] = []> = BiggerThan<B, A> extends true ? R["length"] : Divide<Subtract<A,B>, B, Append<R>>;
至此,四則運(yùn)算實(shí)現(xiàn)完畢。
以上就是typescript type類(lèi)型使用梳理總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于typescript type類(lèi)型的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- typescript返回值類(lèi)型和參數(shù)類(lèi)型的具體使用
- Typescript中函數(shù)類(lèi)型及示例詳解
- typescript類(lèi)型體操及關(guān)鍵字使用示例詳解
- TypeScript中類(lèi)型映射的使用
- TypeScript中d.ts類(lèi)型聲明文件的實(shí)現(xiàn)
- TypeScript中的類(lèi)型運(yùn)算符實(shí)現(xiàn)
- TypeScript基本類(lèi)型 typeof 和keyof案例詳解
- 淺聊一下TypeScript中的4種類(lèi)型守衛(wèi)
- TypeScript 獲取函數(shù)的參數(shù)類(lèi)型、返回值類(lèi)型及定義返回函數(shù)類(lèi)型
相關(guān)文章
typescript難學(xué)嗎?前端有必要學(xué)?該怎么學(xué)typescript
TypeScript代碼與?JavaScript?代碼有非常高的兼容性,無(wú)門(mén)檻,你把?JS?代碼改為?TS?就可以運(yùn)行。TypeScript?應(yīng)該不會(huì)脫離?JavaScript?成為獨(dú)立的語(yǔ)言。學(xué)習(xí)?TypeScript?應(yīng)該主要指的是學(xué)習(xí)它的類(lèi)型系統(tǒng)。2022-12-12TypeScript十大排序算法插入排序?qū)崿F(xiàn)示例詳解
這篇文章主要為大家介紹了TypeScript十大排序算法插入排序?qū)崿F(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02Typescript?extends?關(guān)鍵字繼承類(lèi)型約束及條件類(lèi)型判斷實(shí)現(xiàn)示例解析
這篇文章主要介紹了Typescript?extends?關(guān)鍵字繼承類(lèi)型約束及條件類(lèi)型判斷實(shí)現(xiàn)示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08TypeScript中的聯(lián)合類(lèi)型使用示例詳解
這篇文章主要為大家介紹了TypeScript中的聯(lián)合類(lèi)型使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08typescript快速上手的基礎(chǔ)知識(shí)篇
靜態(tài)類(lèi)型的typescript與傳統(tǒng)動(dòng)態(tài)弱類(lèi)型語(yǔ)言javascript不同,在執(zhí)行前會(huì)先編譯成javascript,因?yàn)樗鼜?qiáng)大的type類(lèi)型系統(tǒng)加持,能讓我們?cè)诰帉?xiě)代碼時(shí)增加更多嚴(yán)謹(jǐn)?shù)南拗?。注意,它并不是一門(mén)全新的語(yǔ)言,所以并沒(méi)有增加額外的學(xué)習(xí)成本2022-12-12TypeScript十大排序算法之選擇排序?qū)崿F(xiàn)示例詳解
這篇文章主要為大家介紹了TypeScript十大排序算法之選擇排序?qū)崿F(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02