掌握 TypeScript 這些官方工具類型棚赔,讓你的開發(fā)事半功倍

我們深入了解 TypeScript 官方提供的全局工具類型。

在 TypeScript 中提供了許多自帶的工具類型徘郭,因為這些類型都是全局可用的靠益,所以無須導入即可直接使用。了解了基礎的工具類型后崎岂,我們不僅知道 TypeScript 如何利用前幾講介紹的基礎類型知識實現(xiàn)這些工具類型捆毫,還知道如何更好地利用這些基礎類型,以免重復造輪子冲甘,并能通過這些工具類型實現(xiàn)更復雜的類型绩卤。

根據(jù)使用范圍途样,我們可以將工具類型劃分為操作接口類型、聯(lián)合類型濒憋、函數(shù)類型何暇、字符串類型這 4 個方向,下面一一介紹凛驮。

操作接口類型

Partial

Partial 工具類型可以將一個類型的所有屬性變?yōu)榭蛇x的裆站,且該工具類型返回的類型是給定類型的所有子集,下面我們看一個具體的示例:

type Partial<T> = {

? [P in keyof T]?: T[P];

};

interface Person {

? name: string;

? age?: number;

? weight?: number;

}

type PartialPerson = Partial<Person>;

// 相當于

interface PartialPerson {

? name?: string;

? age?: number;

? weight?: number;

}

在上述示例中黔夭,我們使用映射類型取出了傳入類型的所有鍵值宏胯,并將其值設定為可選的。

Required

與 Partial 工具類型相反本姥,Required 工具類型可以將給定類型的所有屬性變?yōu)楸靥畹募缗郏旅嫖覀兛匆粋€具體示例。

type Required<T> = {

? [P in keyof T]-?: T[P];

};

type RequiredPerson = Required<Person>;

// 相當于

interface RequiredPerson {

? name: string;

? age: number;

? weight: number;

}

在上述示例中婚惫,映射類型在鍵值的后面使用了一個 - 符號氛赐,- 與 ? 組合起來表示去除類型的可選屬性,因此給定類型的所有屬性都變?yōu)榱吮靥睢?/p>

Readonly

Readonly 工具類型可以將給定類型的所有屬性設為只讀先舷,這意味著給定類型的屬性不可以被重新賦值艰管,下面我們看一個具體的示例。

type Readonly<T> = {

? readonly [P in keyof T]: T[P];

};

type ReadonlyPerson = Readonly<Person>;

// 相當于

interface ReadonlyPerson {

? readonly name: string;

? readonly age?: number;

? readonly weight?: number;

}

在上述示例中蒋川,經(jīng)過 Readonly 處理后牲芋,ReadonlyPerson 的 name、age尔破、weight 等屬性都變成了 readonly 只讀街图。

Pick

Pick 工具類型可以從給定的類型中選取出指定的鍵值,然后組成一個新的類型懒构,下面我們看一個具體的示例餐济。

type Pick<T, K extends keyof T> = {

? [P in K]: T[P];

};

type NewPerson = Pick<Person, 'name' | 'age'>;

// 相當于

interface NewPerson {

? name: string;

? age?: number;

}

在上述示例中,Pick工具類型接收了兩個泛型參數(shù)胆剧,第一個 T 為給定的參數(shù)類型絮姆,而第二個參數(shù)為需要提取的鍵值 key。有了參數(shù)類型和需要提取的鍵值 key秩霍,我們就可以通過映射類型很容易地實現(xiàn) Pick 工具類型的功能篙悯。

Omit

與 Pick 類型相反,Omit 工具類型的功能是返回去除指定的鍵值之后返回的新類型铃绒,下面我們看一個具體的示例:

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

type NewPerson = Omit<Person, 'weight'>;

// 相當于

interface NewPerson {

? name: string;

? age?: number;

}

在上述示例中鸽照,Omit 類型的實現(xiàn)使用了前面介紹的 Pick 類型。我們知道 Pick 類型的作用是選取給定類型的指定屬性颠悬,那么這里的 Omit 的作用應該是選取除了指定屬性之外的屬性矮燎,而 Exclude 工具類型的作用就是從入?yún)?T 屬性的聯(lián)合類型中排除入?yún)?K 指定的若干屬性定血。

Tips:操作接口類型這一小節(jié)所介紹的工具類型都使用了映射類型。通過映射類型诞外,我們可以對原類型的屬性進行重新映射澜沟,從而組成想要的類型。


聯(lián)合類型

Exclude

在介紹 Omit 類型的實現(xiàn)中峡谊,我們使用了 Exclude 類型茫虽。通過使用 Exclude 類型,我們從接口的所有屬性中去除了指定屬性既们,因此濒析,Exclude 的作用就是從聯(lián)合類型中去除指定的類型。

下面我們看一個具體的示例:

type Exclude<T, U> = T extends U ? never : T;

type T = Exclude<'a' | 'b' | 'c', 'a'>; // => 'b' | 'c'

type NewPerson = Omit<Person, 'weight'>;

// 相當于

type NewPerson = Pick<Person, Exclude<keyof Person, 'weight'>>;

// 其中

type ExcludeKeys = Exclude<keyof Person, 'weight'>; // => 'name' | 'age'

在上述示例中啥纸,Exclude 的實現(xiàn)使用了條件類型悼枢。如果類型 T 可被分配給類型 U ,則不返回類型 T脾拆,否則返回此類型 T ,這樣我們就從聯(lián)合類型中去除了指定的類型莹妒。

再回看之前的 NewPerson 類型的例子名船,我們也就很明白了。在 ExcludeKeys 中旨怠,如果 Person 類型的屬性是我們要去除的屬性渠驼,則不返回該屬性,否則返回其類型鉴腻。

Extract

Extract 類型的作用與 Exclude 正好相反迷扇,Extract 主要用來從聯(lián)合類型中提取指定的類型,類似于操作接口類型中的 Pick 類型爽哎。

下面我們看一個具體的示例:

type Extract<T, U> = T extends U ? T : never;

type T = Extract<'a' | 'b' | 'c', 'a'>; // => 'a'

通過上述示例蜓席,我們發(fā)現(xiàn) Extract 類型相當于取出兩個聯(lián)合類型的交集。

此外课锌,我們還可以基于 Extract 實現(xiàn)一個獲取接口類型交集的工具類型厨内,如下示例:

type Intersect<T, U> = {

? [K in Extract<keyof T, keyof U>]: T[K];

};

interface Person {

? name: string;

? age?: number;

? weight?: number;

}

interface NewPerson {

? name: string;

? age?: number;

}

type T = Intersect<Person, NewPerson>;

// 相當于

type T = {

? name: string;

? age?: number;

};

在上述的例子中,我們使用了 Extract 類型來提取兩個接口類型屬性的交集渺贤,并使用映射類型生成了一個新的類型雏胃。

NonNullable

NonNullable 的作用是從聯(lián)合類型中去除 null 或者 undefined 的類型。如果你對條件類型已經(jīng)很熟悉了志鞍,那么應該知道如何實現(xiàn) NonNullable 類型了瞭亮。

下面看一個具體的示例:

type NonNullable<T> = T extends null | undefined ? never : T;

// 等同于使用 Exclude

type NonNullable<T> = Exclude<T, null | undefined>;

type T = NonNullable<string | number | undefined | null>; // => string | number

在上述示例中,如果 NonNullable 傳入的類型可以被分配給 null 或是 undefined 固棚,則不返回該類型统翩,否則返回其具體類型仙蚜。

Record

Record 的作用是生成接口類型,然后我們使用傳入的泛型參數(shù)分別作為接口類型的屬性和值唆缴。

下面我們看一個具體的示例:

type Record<K extends keyof any, T> = {

? [P in K]: T;

};

type MenuKey = 'home' | 'about' | 'more';

interface Menu {

? label: string;

? hidden?: boolean;

}

const menus: Record<MenuKey, Menu> = {

? about: { label: '關(guān)于' },

? home: { label: '主頁' },

? more: { label: '更多', hidden: true },

};

在上述示例中鳍征,Record 類型接收了兩個泛型參數(shù):第一個參數(shù)作為接口類型的屬性,第二個參數(shù)作為接口類型的屬性值面徽。

需要注意:這里的實現(xiàn)限定了第一個泛型參數(shù)繼承自keyof any艳丛。

在 TypeScript 中,keyof any 指代可以作為對象鍵的屬性趟紊,如下示例:

type T = keyof any; // => string | number | symbol

說明:目前氮双,JavaScript 僅支持string、number霎匈、symbol作為對象的鍵值戴差。


函數(shù)類型

ConstructorParameters

ConstructorParameters 可以用來獲取構(gòu)造函數(shù)的構(gòu)造參數(shù),而 ConstructorParameters 類型的實現(xiàn)則需要使用 infer 關(guān)鍵字推斷構(gòu)造參數(shù)的類型铛嘱。

關(guān)于 infer 關(guān)鍵字暖释,我們可以把它當成簡單的模式匹配來看待。如果真實的參數(shù)類型和 infer 匹配的一致墨吓,那么就返回匹配到的這個類型球匕。

下面看一個具體的示例:

type ConstructorParameters<T extends new (...args: any) => any> = T extends new (

? ...args: infer P

) => any

? ? P

? : never;

class Person {

? constructor(name: string, age?: number) {}

}

type T = ConstructorParameters<typeof Person>; // [name: string, age?: number]

在上述示例中,ConstructorParameters 泛型接收了一個參數(shù)帖烘,并且限制了這個參數(shù)需要實現(xiàn)構(gòu)造函數(shù)亮曹。于是,我們通過 infer 關(guān)鍵字匹配了構(gòu)造函數(shù)內(nèi)的構(gòu)造參數(shù)秘症,并返回了這些參數(shù)照卦。因此,可以看到第 7 行匹配了 Person 構(gòu)造函數(shù)的兩個參數(shù)乡摹,并返回了一個元組類型 [string, number] 給類型別名 T役耕。

Parameters

Parameters 的作用與 ConstructorParameters 類似,Parameters 可以用來獲取函數(shù)的參數(shù)并返回序?qū)Υ狭缦率纠?/p>

type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

type T0 = Parameters<() => void>; // []

type T1 = Parameters<(x: number, y?: string) => void>; // [x: number, y?: string]

在上述示例中蹄葱,Parameters 的泛型參數(shù)限制了傳入的類型需要滿足函數(shù)類型。

ReturnType

ReturnType 的作用是用來獲取函數(shù)的返回類型锄列,下面我們看一個具體的示例:

type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

type T0 = ReturnType<() => void>; // => void

type T1 = ReturnType<() => string>; // => string

在上述示例中图云,ReturnType的泛型參數(shù)限制了傳入的類型需要滿足函數(shù)類型。

ThisParameterType

ThisParameterType 可以用來獲取函數(shù)的 this 參數(shù)類型邻邮,下面看一個具體的示例:

type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown;

type T = ThisParameterType<(this: Number, x: number) => void>; // Number

在上述示例的第 1 行中竣况,因為函數(shù)類型的第一個參數(shù)聲明的是 this 參數(shù)類型,所以我們可以直接使用 infer 關(guān)鍵字進行匹配并獲取 this 參數(shù)類型筒严。在示例的第 2 行丹泉,類型別名 T 得到的類型就是 Number情萤。

ThisType

ThisType 的作用是可以在對象字面量中指定 this 的類型。ThisType 不返回轉(zhuǎn)換后的類型摹恨,而是通過 ThisType 的泛型參數(shù)指定 this 的類型筋岛,下面看一個具體的示例:

注意:如果你想使用這個工具類型,那么需要開啟noImplicitThis的 TypeScript 配置晒哄。

type ObjectDescriptor<D, M> = {

? data?: D;

? methods?: M & ThisType<D & M>; // methods 中 this 的類型是 D & M

};

function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {

? let data: object = desc.data || {};

? let methods: object = desc.methods || {};

? return { ...data, ...methods } as D & M;

}

const obj = makeObject({

? data: { x: 0, y: 0 },

? methods: {

? ? moveBy(dx: number, dy: number) {

? ? ? this.x += dx; // this => D & M

? ? ? this.y += dy; // this => D & M

? ? },

? },

});

obj.x = 10;

obj.y = 20;

obj.moveBy(5, 5);

在上述示例子中睁宰,methods 屬性的 this 類型為 D & M,在上下文中指代 { x: number, y: number } & { moveBy(dx: number, dy: number): void }寝凌。

ThisType 工具類型只是提供了一個空的泛型接口柒傻,僅可以在對象字面量上下文中被 TypeScript 識別,如下所示:

interface ThisType<T> {}

也就是說該類型的作用相當于任意空接口较木。

OmitThisParameter

OmitThisParameter 工具類型主要用來去除函數(shù)類型中的 this 類型红符。如果傳入的函數(shù)類型沒有顯式聲明 this 類型,那么返回的仍是原來的函數(shù)類型伐债。

下面看一個具體的示例:

type OmitThisParameter<T> = unknown extends ThisParameterType<T>

? ? T

? : T extends (...args: infer A) => infer R

? ? (...args: A) => R

? : T;

type T = OmitThisParameter<(this: Number, x: number) => string>; // (x: number) => string

在上述示例中预侯, ThisParameterType 類型的實現(xiàn)如果傳入的泛型參數(shù)無法推斷 this 的類型,則會返回 unknown 類型峰锁。在OmitThisParameter 的實現(xiàn)中雌桑,第一個條件語句如果傳入的函數(shù)參數(shù)沒有 this 類型,則返回原類型祖今;否則通過 infer 分別獲取函數(shù)參數(shù)和返回值的類型構(gòu)造一個新的沒有 this 的函數(shù)類型,并返回這個函數(shù)類型拣技。


字符串類型

模板字符串

TypeScript 自 4.1版本起開始支持模板字符串字面量類型千诬。為此,TypeScript 也提供了 Uppercase膏斤、Lowercase徐绑、Capitalize、Uncapitalize這 4 種內(nèi)置的操作字符串的類型莫辨,如下示例:

// 轉(zhuǎn)換字符串字面量到大寫字母

type Uppercase<S extends string> = intrinsic;

// 轉(zhuǎn)換字符串字面量到小寫字母

type Lowercase<S extends string> = intrinsic;

// 轉(zhuǎn)換字符串字面量的第一個字母為大寫字母

type Capitalize<S extends string> = intrinsic;

// 轉(zhuǎn)換字符串字面量的第一個字母為小寫字母

type Uncapitalize<S extends string> = intrinsic;

type T0 = Uppercase<'Hello'>; // => 'HELLO'

type T1 = Lowercase<T0>; // => 'hello'

type T2 = Capitalize<T1>; // => 'Hello'

type T3 = Uncapitalize<T2>; // => 'hello'

在上述示例中傲茄,這 4 種操作字符串字面量工具類型的實現(xiàn)都是使用 JavaScript 運行時的字符串操作函數(shù)計算出來的,且不支持語言區(qū)域設置沮榜。以下代碼是這 4 種字符串工具類型的實際實現(xiàn)盘榨。

function applyStringMapping(symbol: Symbol, str: string) {

? switch (intrinsicTypeKinds.get(symbol.escapedName as string)) {

? ? case IntrinsicTypeKind.Uppercase:

? ? ? return str.toUpperCase();

? ? case IntrinsicTypeKind.Lowercase:

? ? ? return str.toLowerCase();

? ? case IntrinsicTypeKind.Capitalize:

? ? ? return str.charAt(0).toUpperCase() + str.slice(1);

? ? case IntrinsicTypeKind.Uncapitalize:

? ? ? return str.charAt(0).toLowerCase() + str.slice(1);

? }

? return str;

}

在上述代碼中可以看到,字符串的轉(zhuǎn)換使用了 JavaScript 中字符串的 toUpperCase 和 toLowerCase 方法蟆融,而不是 toLocaleUpperCase 和 toLocaleLowerCase草巡。其中 toUpperCase 和 toLowerCase 采用的是 Unicode 編碼默認的大小寫轉(zhuǎn)換規(guī)則。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末型酥,一起剝皮案震驚了整個濱河市山憨,隨后出現(xiàn)的幾起案子查乒,更是在濱河造成了極大的恐慌,老刑警劉巖郁竟,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件玛迄,死亡現(xiàn)場離奇詭異,居然都是意外死亡棚亩,警方通過查閱死者的電腦和手機蓖议,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蔑舞,“玉大人拒担,你說我怎么就攤上這事」パ” “怎么了从撼?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長钧栖。 經(jīng)常有香客問我低零,道長,這世上最難降的妖魔是什么拯杠? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任掏婶,我火速辦了婚禮,結(jié)果婚禮上潭陪,老公的妹妹穿的比我還像新娘雄妥。我一直安慰自己,他們只是感情好依溯,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布老厌。 她就那樣靜靜地躺著,像睡著了一般黎炉。 火紅的嫁衣襯著肌膚如雪枝秤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天慷嗜,我揣著相機與錄音淀弹,去河邊找鬼。 笑死庆械,一個胖子當著我的面吹牛薇溃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播缭乘,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼痊焊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起薄啥,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤辕羽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后垄惧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體刁愿,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年到逊,在試婚紗的時候發(fā)現(xiàn)自己被綠了铣口。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡觉壶,死狀恐怖脑题,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情铜靶,我是刑警寧澤叔遂,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站争剿,受9級特大地震影響已艰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蚕苇,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一哩掺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧涩笤,春花似錦嚼吞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锰茉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間切心,已是汗流浹背飒筑。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留绽昏,地道東北人协屡。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像全谤,于是被迫代替她去往敵國和親肤晓。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容