摘要:背景大家用過都清楚,很多時候我們需要提前聲明一個類型,再將類型賦予變量。上面使用到的和都是內(nèi)置的類型別名。下面給大家介紹一下常用的內(nèi)置類型,以及自行拓展的類型。
背景
大家用過 Typescript 都清楚,很多時候我們需要提前聲明一個類型,再將類型賦予變量。
例如在業(yè)務(wù)中,我們需要渲染一個表格,往往需要定義:
interface Row { user: string email: string id: number vip: boolean // ... } const tableDatas: Row[] = [] // ...
有時候我們也需要表格對應(yīng)的搜索表單,需要其中一兩個搜索項,如果剛接觸 typescript 的同學(xué)可能會立刻這樣寫:
interface SearchModel { user?: string id?: number } const model: SearchModel = { user: "", id: undefined }
這樣寫會出現(xiàn)一個問題,如果后面id 類型要改成 string,我們需要改 2 處地方,不小心的話可能就會忘了改另外一處。所以,有些人會這樣寫:
interface SearchModel { user?: Row["user"] id?: Row["id"] }
這固然是一個解決方法,但事實上,我們前面已經(jīng)定義了 Row 類型,這其實是可以更優(yōu)雅地復(fù)用的:
const model: Partial= { user: "", id: undefined } // 或者需要明確指定 key 的,可以 const model2: Partial
>
這樣一來,很多情況下,我們可以盡量少地寫重復(fù)的類型,復(fù)用已有類型,讓代碼更加優(yōu)雅容易維護(hù)。
上面使用到的 Partial 和 Pick 都是 typescript 內(nèi)置的類型別名。下面給大家介紹一下 typescript 常用的內(nèi)置類型,以及自行拓展的類型。
typescript 內(nèi)置類型 Partial將類型 T 的所有屬性標(biāo)記為可選屬性
type Partial= { [P in keyof T]?: T[P]; };
使用場景:
// 賬號屬性 interface AccountInfo { name: string email: string age: number vip: 0|1 // 1 是vip ,0 是非vip } // 當(dāng)我們需要渲染一個賬號表格時,我們需要定義 const accountList: AccountInfo[] = [] // 但當(dāng)我們需要查詢過濾賬號信息,需要通過表單, // 但明顯我們可能并不一定需要用到所有屬性進(jìn)行搜索,此時可以定義 const model: PartialRequired= { name: "", vip: undefind }
與 Partial 相反,Required 將類型 T 的所有屬性標(biāo)記為必選屬性
type RequiredReadonly= { [P in keyof T]-?: T[P]; };
將所有屬性標(biāo)記為 readonly, 即不能修改
type ReadonlyPick= { readonly [P in keyof T]: T[P]; };
從 T 中過濾出屬性 K
type Pick= { [P in K]: T[P]; };
使用場景:
interface AccountInfo { name: string email: string age: number vip?: 0|1 // 1 是vip ,0 是非vip } type CoreInfo = PickRecord/* { name: string email: stirng } */
標(biāo)記對象的 key value類型
type Record= { [P in K]: T; };
使用場景:
// 定義 學(xué)號(key)-賬號信息(value) 的對象 const accountMap: Record= { 10001: { name: "xx", email: "xxxxx", // ... } } const user: Record<"name"|"email", string> = { name: "", email: "" }
// 復(fù)雜點的類型推斷 function mapObjectExclude(obj: Record , f: (x: T) => U): Record const names = { foo: "hello", bar: "world", baz: "bye" }; // 此處推斷 K, T 值為 string , U 為 number const lengths = mapObject(names, s => s.length); // { foo: number, bar: number, baz: number }
移除 T 中的 U 屬性
type Exclude= T extends U ? never : T;
使用場景:
// "a" | "d" type A = Exclude<"a"|"b"|"c"|"d" ,"b"|"c"|"e" >
乍一看好像這個沒啥卵用,但是,我們通過一番操作,之后就可以得到 Pick 的反操作:
type OmitExtract= Pick > type NonCoreInfo = Omit /* { age: number vip: 0|1, } */
Exclude 的反操作,取 T,U兩者的交集屬性
type Extract= T extends U ? T : never;
使用 demo:
// "b"|"c" type A = Extract<"a"|"b"|"c"|"d" ,"b"|"c"|"e" >
這個看起來沒啥用,實際上還真沒啥卵用,應(yīng)該是我才疏學(xué)淺,還沒發(fā)掘到其用途。
排除類型 T 的 null | undefined 屬性
type NonNullable= T extends null | undefined ? never : T;
使用 demo
type A = string | number | undefined type B = NonNullable // string | number function f2Parameters(x: T, y: NonNullable ) { let s1: string = x; // Error, x 可能為 undefined let s2: string = y; // Ok }
獲取一個函數(shù)的所有參數(shù)類型
// 此處使用 infer P 將參數(shù)定為待推斷類型 // T 符合函數(shù)特征時,返回參數(shù)類型,否則返回 never type Parametersany> = T extends (...args: infer P) => any ? P : never;
使用demo:
interface IFunc { (person: IPerson, count: number): boolean } type P = Parameters// [IPerson, number] const person01: P[0] = { // ... }
另一種使用場景是,快速獲取未知函數(shù)的參數(shù)類型
import {somefun} from "somelib" // 從其他庫導(dǎo)入的一個函數(shù),獲取其參數(shù)類型 type SomeFuncParams = ParametersConstructorParameters// 內(nèi)置函數(shù) // [any, number?, number?] type FillParams = Parameters
類似于 Parameters
type ConstructorParametersany> = T extends new (...args: infer P) => any ? P : never;
使用 demo:
// string | number | Date type DateConstrParams = ConstructorParametersReturnType
獲取函數(shù)類型 T 的返回類型
type ReturnTypeany> = T extends (...args: any) => infer R ? R : any;
使用方式和 Parameters
獲取一個類的返回類型
type InstanceTypeany> = T extends new (...args: any) => infer R ? R : any;
使用方式和 ConstructorParameters
使用 typescript 有時候需要重寫一個庫提供的 interface 的某個屬性,但是重寫 interface 有可能會導(dǎo)致沖突:
interface Test { name: string say(word: string): string } interface Test2 extends Test{ name: Test["name"] | number } // error: Type "string | number" is not assignable to type "string".
那么可以通過一些 type 來曲線救國實現(xiàn)我們的需求:
// 原理是,將 類型 T 的所有 K 屬性置為 any, // 然后自定義 K 屬性的類型, // 由于任何類型都可以賦予 any,所以不會產(chǎn)生沖突 type Weaken數(shù)組 轉(zhuǎn)換 成 union= { [P in keyof T]: P extends K ? any : T[P]; }; interface Test2 extends Weaken { name: Test["name"] | number } // ok
有時候需要
const ALL_SUITS = ["hearts", "diamonds", "spades", "clubs"] as const; // TS 3.4 type SuitTuple = typeof ALL_SUITS; // readonly ["hearts", "diamonds", "spades", "clubs"] type Suit = SuitTuple[number]; // union type : "hearts" | "diamonds" | "spades" | "clubs"根據(jù) enum 生成 union
enum 的 key 值 union
enum Weekday { Mon = 1 Tue = 2 Wed = 3 } type WeekdayName = keyof typeof Weekday // "Mon" | "Tue" | "Wed"
enum 無法實現(xiàn)value-union , 但可以 object 的 value 值 union
const lit =PartialRecord(v: V) => v; const Weekday = { MONDAY: lit(1), TUESDAY: lit(2), WEDNESDAY: lit(3) } type Weekday = (typeof Weekday)[keyof typeof Weekday] // 1|2|3
前面我們講到了 Record 類型,我們會常用到
interface Model { name: string email: string id: number age: number } // 定義表單的校驗規(guī)則 const validateRules: Record= { name: {required: true, trigger: `blur`}, id: {required: true, trigger: `blur`}, email: {required: true, message: `...`}, // error: Property age is missing in type... }
這里出現(xiàn)了一個問題,validateRules 的 key 值必須和 Model 全部匹配,缺一不可,但實際上我們的表單可能只有其中的一兩項,這時候我們就需要:
type PartialRecord= Partial > const validateRules: PartialRecord = { name: {required: true, trigger: `blur`} }
這個例子組合使用了 typescript 內(nèi)置的 類型別名 Partial 和 Partial。
Unpacked解壓抽離關(guān)鍵類型
type Unpacked總結(jié)= T extends (infer U)[] ? U : T extends (...args: any[]) => infer U ? U : T extends Promise ? U : T; type T0 = Unpacked ; // string type T1 = Unpacked ; // string type T2 = Unpacked<() => string>; // string type T3 = Unpacked >; // string type T4 = Unpacked []>; // Promise type T5 = Unpacked []>>; // string
事實上,基于已有的類型別名,還有新推出的 infer 待推斷類型,可以探索出各種各樣的復(fù)雜組合玩法,這里不再多說,大家可以慢慢探索。
感謝閱讀!
本文首發(fā)于 github 博客
如文章對你有幫助,你的 star 是對我最大的支持
插播廣告:
深圳 Shopee 長期內(nèi)推
崗位:前端,后端(要轉(zhuǎn)go),產(chǎn)品,UI,測試,安卓,IOS,運維 全都要。
薪酬福利:20K-50K
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/104449.html
摘要:本文就來說下這幾個字段的使用場景,以及同時存在這幾個字段時,他們之間的優(yōu)先級。當(dāng)存在和這種同名不同后綴的文件時,或者是會優(yōu)先加載文件的?;蛘邇?yōu)先級是通過直接執(zhí)行腳本只有字段有效。 browser VS module VS main 前端開發(fā)中使用到 npm 包那可算是家常便飯,而使用到 npm 包總免不了接觸到 package.json 包配置文件。 那么這里就有...
摘要:怎么影響了我的思考方式對前端開發(fā)者來說,能強化了面向接口編程這一理念。使用的過程就是在加深理解的過程,確實面向接口編程天然和靜態(tài)類型更為親密。 電影《降臨》中有一個觀點,語言會影響人的思維方式,對于前端工程師來說,使用 typescript 開發(fā)無疑就是在嘗試換一種思維方式做事情。 其實直到最近,我才開始系統(tǒng)的學(xué)習(xí) typescript ,前后大概花了一個月左右的時間。在這之前,我也在...
摘要:怎么影響了我的思考方式對前端開發(fā)者來說,能強化了面向接口編程這一理念。使用的過程就是在加深理解的過程,確實面向接口編程天然和靜態(tài)類型更為親密。摘要: 學(xué)會TS思考方式。 原文:TypeScript - 一種思維方式 作者:zhangwang Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所有。 電影《降臨》中有一個觀點,語言會影響人的思維方式,對于前端工程師來說,使用 typescript 開...
摘要:軟件跨平臺支持以及,運行流暢,可謂是微軟的良心之作微軟有這個宇宙最強,自然也不會弱宇宙最強編輯器說到代碼編輯器,我們有必要提一提還有。 原文鏈接:VS Code上手與超實用插件安利 工欲善其事必先利其器 Visual Studio Code (簡稱 VS Code / VSC) 是一款免費開源的現(xiàn)代化輕量級代碼編輯器,支持幾乎所有主流的開發(fā)語言的語法高亮、智能代碼補全、自定義熱鍵、括號...
摘要:但是,如果必須更改實現(xiàn)方法以指向不同的數(shù)據(jù)庫,則單元測試將失敗,因為它們是耦合邏輯的實現(xiàn)細(xì)節(jié)。 showImg(https://segmentfault.com/img/bVbwf0d?w=786&h=155); showImg(https://segmentfault.com/img/bVbwf8m?w=941&h=578); React是一個用于構(gòu)建用戶界面的JavaScript庫...
閱讀 1978·2021-11-23 09:51
閱讀 889·2021-11-19 09:40
閱讀 838·2021-10-27 14:20
閱讀 5033·2021-10-09 09:52
閱讀 3310·2021-10-09 09:44
閱讀 1739·2021-10-08 10:05
閱讀 5109·2021-09-09 11:47
閱讀 3488·2019-08-30 12:47