用了 TS 映射類(lèi)型,同事直呼內(nèi)行!
在日常工作中,用戶注冊(cè)是一個(gè)很常見(jiàn)的場(chǎng)景。這里我們可以使用 TS 定義一個(gè) User 類(lèi)型,在該類(lèi)型中所有的鍵都是必填的。
type?User?=?{
??name:?string;?//?姓名
??password:?string;?//?密碼
??address:?string;?//?地址
??phone:?string;?//?聯(lián)系電話
};
通常情況下,對(duì)于已注冊(cè)的用戶,我們是允許用戶只修改部分用戶信息。這時(shí)我們就可以定義一個(gè)新的 UserPartial 類(lèi)型,表示用于更新的用戶對(duì)象的類(lèi)型,在該類(lèi)型中所有的鍵都是可選的。
type?UserPartial?=?{
??name?:?string;?//?姓名
??password?:?string;?//?密碼
??address?:?string;?//?地址
??phone?:?string;?//?聯(lián)系電話
};

而對(duì)于查看用戶信息的場(chǎng)景,我們希望該用戶對(duì)象所對(duì)應(yīng)的對(duì)象類(lèi)型中所有的鍵都是只讀。針對(duì)這種需求,我們可以定義 ReadonlyUser 類(lèi)型。
type?ReadonlyUser?=?{
??readonly?name:?string;?//?姓名
??readonly?password:?string;?//?密碼
??readonly?address:?string;?//?地址
??readonly?phone:?string;?//?聯(lián)系電話
};

回顧前面已定義的與用戶相關(guān)的 3 種類(lèi)型,你會(huì)發(fā)現(xiàn)它們中含有很多重復(fù)的代碼。

那么如何減少以上類(lèi)型中的重復(fù)代碼呢?答案是可以使用映射類(lèi)型,它是一種泛型類(lèi)型,可用于把原有的對(duì)象類(lèi)型映射成新的對(duì)象類(lèi)型。


映射類(lèi)型的語(yǔ)法如下:
{?[?P?in?K?]?:?T?}
其中 P in K 類(lèi)似于 JavaScript 中的?for...in?語(yǔ)句,用于遍歷 K 類(lèi)型中的所有類(lèi)型,而 T 類(lèi)型變量用于表示 TS 中的任意類(lèi)型。
在映射的過(guò)程中,你還可以使用?readonly?和???這兩個(gè)額外的修飾符。通過(guò)添加?+?和?-?前綴,來(lái)增加和移除對(duì)應(yīng)的修飾符。如果沒(méi)有添加任何前綴的話,默認(rèn)是使用?+。

現(xiàn)在我們就可以總結(jié)出常見(jiàn)的映射類(lèi)型語(yǔ)法:
{?[?P?in?K?]?:?T?}
{?[?P?in?K?]??:?T?}
{?[?P?in?K?]?-?:?T?}
{?readonly?[?P?in?K?]?:?T?}
{?readonly?[?P?in?K?]??:?T?}
{?-readonly?[?P?in?K?]??:?T?}
介紹完映射類(lèi)型的語(yǔ)法,我們來(lái)看一些具體的例子:
type?Item?=?{?a:?string;?b:?number;?c:?boolean?};
type?T1?=?{?[P?in?"x"?|?"y"]:?number?};?//?{?x:?number,?y:?number?}
type?T2?=?{?[P?in?"x"?|?"y"]:?P?};?//?{?x:?"x",?y:?"y"?}
type?T3?=?{?[P?in?"a"?|?"b"]:?Item[P]?};?//?{?a:?string,?b:?number?}
type?T4?=?{?[P?in?keyof?Item]:?Item[P]?};?//?{?a:?string,?b:?number,?c:?boolean?}
下面我們來(lái)看一下如何利用映射類(lèi)型來(lái)重新定義 UserPartial 類(lèi)型:
type?MyPartial?=?{
??[P?in?keyof?T]?:?T[P];
};
type?UserPartial?=?MyPartial;
在以上代碼中,我們定義了 MyPartial 映射類(lèi)型,然后利用該類(lèi)型把 User 類(lèi)型映射成 UserPartial 類(lèi)型。

其中 keyof 操作符用于獲取某種類(lèi)型中的所有鍵,其返回類(lèi)型是聯(lián)合類(lèi)型。而類(lèi)型變量 P 會(huì)隨著每次遍歷改變成不同的類(lèi)型,T[P]?該語(yǔ)法類(lèi)似于屬性訪問(wèn)的語(yǔ)法,用于獲取對(duì)象類(lèi)型某個(gè)屬性對(duì)應(yīng)值的類(lèi)型。
TypeScript 4.1 版本允許我們使用?as 子句對(duì)映射類(lèi)型中的鍵進(jìn)行重新映射。它的語(yǔ)法如下:
type?MappedTypeWithNewKeys?=?{
????[K?in?keyof?T?as?NewKeyType]:?T[K]
????//????????????^^^^^^^^^^^^^
????//????????????這是新的語(yǔ)法!
}

其中 NewKeyType 的類(lèi)型必須是?string | number | symbol?聯(lián)合類(lèi)型的子類(lèi)型。使用 as 子句,我們可以定義一個(gè) Getters 工具類(lèi)型,用于為對(duì)象類(lèi)型生成對(duì)應(yīng)的 Getter 類(lèi)型:
type?Getters?=?{
??[K?in?keyof?T?as?`get${Capitalize<string?&?K>}`]:?()?=>?T[K]
};
interface?Person?{
????name:?string;
????age:?number;
????location:?string;
}
type?LazyPerson?=?Getters;
//?{
//???getName:?()?=>?string;
//???getAge:?()?=>?number;
//???getLocation:?()?=>?string;
//?}
在以上代碼中,因?yàn)?keyof ?T 返回的類(lèi)型可能會(huì)包含 symbol 類(lèi)型,而 Capitalize 工具類(lèi)型要求處理的類(lèi)型需要是 string 類(lèi)型的子類(lèi)型,所以需要通過(guò)交叉運(yùn)算符進(jìn)行類(lèi)型過(guò)濾。
此外,在對(duì)鍵進(jìn)行重新映射的過(guò)程中,我們可以通過(guò)返回?never?類(lèi)型對(duì)鍵進(jìn)行過(guò)濾:
//?Remove?the?'kind'?property
type?RemoveKindField?=?{
????[K?in?keyof?T?as?Exclude"kind">]:?T[K]
};
interface?Circle?{
????kind:?"circle";
????radius:?number;
}
type?KindlessCircle?=?RemoveKindField;
//???type?KindlessCircle?=?{
//???????radius:?number;
//???};
看完本文之后,相信你已經(jīng)了解映射類(lèi)型的作用了,也知道 TS 內(nèi)部一些工具類(lèi)型是如何實(shí)現(xiàn)的。你喜歡以這種形式學(xué) TS 么?喜歡的話,記得點(diǎn)贊與收藏喲。
?? 謝謝支持
以上便是本次分享的全部?jī)?nèi)容,希望對(duì)你有所幫助^_^
喜歡的話別忘了?分享、點(diǎn)贊、收藏?三連哦~。
