TypeScript高級(jí)類(lèi)型入門(mén)手冊(cè):附大量代碼實(shí)例(收藏?。?/h1>
點(diǎn)擊上方 前端Q,關(guān)注公眾號(hào)
回復(fù)加群,加入前端Q技術(shù)交流群
原文:https://juejin.cn/post/6904150785966211086
作者:望道
TypeScript 是一種類(lèi)型化的語(yǔ)言,允許你指定變量的類(lèi)型,函數(shù)參數(shù),返回的值和對(duì)象屬性。
你可以把本文看做一個(gè)帶有示例的 TypeScript 高級(jí)類(lèi)型備忘單
讓我們開(kāi)始吧!
Intersection Types(交叉類(lèi)型)
交叉類(lèi)型是一種將多種類(lèi)型組合為一種類(lèi)型的方法。這意味著你可以將給定的類(lèi)型 A 與類(lèi)型 B 或更多類(lèi)型合并,并獲得具有所有屬性的單個(gè)類(lèi)型。
type LeftType = {
id: number;
left: string;
};
type RightType = {
id: number;
right: string;
};
type IntersectionType = LeftType & RightType;
function showType(args: IntersectionType) {
console.log(args);
}
showType({ id: 1, left: 'test', right: 'test' });
// Output: {id: 1, left: "test", right: "test"}
如你所見(jiàn),IntersectionType組合了兩種類(lèi)型-LeftType和RightType,并使用&符號(hào)形成了交叉類(lèi)型。
Union Types(聯(lián)合類(lèi)型)
聯(lián)合類(lèi)型使你可以賦予同一個(gè)變量不同的類(lèi)型
type UnionType = string | number;
function showType(arg: UnionType) {
console.log(arg);
}
showType('test');
// Output: test
showType(7);
// Output: 7
函數(shù)showType是一個(gè)聯(lián)合類(lèi)型函數(shù),它接受字符串或者數(shù)字作為參數(shù)。
Generic Types(泛型)
泛型類(lèi)型是復(fù)用給定類(lèi)型的一部分的一種方式。它有助于捕獲作為參數(shù)傳遞的類(lèi)型 T。
優(yōu)點(diǎn): 創(chuàng)建可重用的函數(shù),一個(gè)函數(shù)可以支持多種類(lèi)型的數(shù)據(jù)。這樣開(kāi)發(fā)者就可以根據(jù)自己的數(shù)據(jù)類(lèi)型來(lái)使用函數(shù)
泛型函數(shù)
function showType<T>(args: T) {
console.log(args);
}
showType('test');
// Output: "test"
showType(1);
// Output: 1
如何創(chuàng)建泛型類(lèi)型:需要使用<>并將 T(名稱(chēng)可自定義)作為參數(shù)傳遞。上面的 ?? 栗子中, 我們給 showType 添加了類(lèi)型變量 T。T幫助我們捕獲用戶傳入的參數(shù)的類(lèi)型(比如:number/string)之后我們就可以使用這個(gè)類(lèi)型
我們把 showType 函數(shù)叫做泛型函數(shù),因?yàn)樗梢赃m用于多個(gè)類(lèi)型
泛型接口
interface GenericType<T> {
id: number;
name: T;
}
function showType(args: GenericType<string>) {
console.log(args);
}
showType({ id: 1, name: 'test' });
// Output: {id: 1, name: "test"}
function showTypeTwo(args: GenericType<number>) {
console.log(args);
}
showTypeTwo({ id: 1, name: 4 });
// Output: {id: 1, name: 4}
在上面的栗子中,聲明了一個(gè) GenericType 接口,該接口接收泛型類(lèi)型 T, 并通過(guò)類(lèi)型 T來(lái)約束接口內(nèi) name 的類(lèi)型
注:泛型變量約束了整個(gè)接口后,在實(shí)現(xiàn)的時(shí)候,必須指定一個(gè)類(lèi)型
因此在使用時(shí)我們可以將name設(shè)置為任意類(lèi)型的值,示例中為字符串或數(shù)字
多參數(shù)的泛型類(lèi)型
interface GenericType<T, U> {
id: T;
name: U;
}
function showType(args: GenericType<number, string>) {
console.log(args);
}
showType({ id: 1, name: 'test' });
// Output: {id: 1, name: "test"}
function showTypeTwo(args: GenericType<string, string[]>) {
console.log(args);
}
showTypeTwo({ id: '001', name: ['This', 'is', 'a', 'Test'] });
// Output: {id: "001", name: Array["This", "is", "a", "Test"]}
泛型類(lèi)型可以接收多個(gè)參數(shù)。在上面的代碼中,我們傳入兩個(gè)參數(shù):T和U,然后將它們用作id,name的類(lèi)型。也就是說(shuō),我們現(xiàn)在可以使用該接口并提供不同的類(lèi)型作為參數(shù)。
Utility Types
TypeScript 內(nèi)部也提供了很多方便實(shí)用的工具,可幫助我們更輕松地操作類(lèi)型。如果要使用它們,你需要將類(lèi)型傳遞給<>
Partial
-
Partial<T>
Partial 允許你將T類(lèi)型的所有屬性設(shè)為可選。它將在每一個(gè)字段后面添加一個(gè)?。
interface PartialType {
id: number;
firstName: string;
lastName: string;
}
/*
等效于
interface PartialType {
id?: number
firstName?: string
lastName?: string
}
*/
function showType(args: Partial<PartialType>) {
console.log(args);
}
showType({ id: 1 });
// Output: {id: 1}
showType({ firstName: 'John', lastName: 'Doe' });
// Output: {firstName: "John", lastName: "Doe"}
上面代碼中聲明了一個(gè)PartialType接口,它用作函數(shù)showType()的參數(shù)的類(lèi)型。為了使所有字段都變?yōu)榭蛇x,我們使用Partial關(guān)鍵字并將PartialType類(lèi)型作為參數(shù)傳遞。
Required
-
Required<T>
將某個(gè)類(lèi)型里的屬性全部變?yōu)楸剡x項(xiàng)
interface RequiredType {
id: number;
firstName?: string;
lastName?: string;
}
function showType(args: Required<RequiredType>) {
console.log(args);
}
showType({ id: 1, firstName: 'John', lastName: 'Doe' });
// Output: { id: 1, firstName: "John", lastName: "Doe" }
showType({ id: 1 });
// Error: Type '{ id: number: }' is missing the following properties from type 'Required<RequiredType>': firstName, lastName
上面的代碼中,即使我們?cè)谑褂媒涌谥跋葘⒛承傩栽O(shè)為可選,但Required被加入后也會(huì)使所有屬性成為必選。如果省略某些必選參數(shù),TypeScript 將報(bào)錯(cuò)。
Readonly
-
Readonly<T>
會(huì)轉(zhuǎn)換類(lèi)型的所有屬性,以使它們無(wú)法被修改
interface ReadonlyType {
id: number;
name: string;
}
function showType(args: Readonly<ReadonlyType>) {
args.id = 4;
console.log(args);
}
showType({ id: 1, name: 'Doe' });
// Error: Cannot assign to 'id' because it is a read-only property.
我們使用Readonly來(lái)使ReadonlyType的屬性不可被修改。也就是說(shuō),如果你嘗試為這些字段之一賦予新值,則會(huì)引發(fā)錯(cuò)誤。
除此之外,你還可以在指定的屬性前面使用關(guān)鍵字readonly使其無(wú)法被重新賦值
interface ReadonlyType {
readonly id: number;
name: string;
}
Pick
-
Pick<T, K>
此方法允許你從一個(gè)已存在的類(lèi)型 T中選擇一些屬性作為K, 從而創(chuàng)建一個(gè)新類(lèi)型
即 抽取一個(gè)類(lèi)型/接口中的一些子集作為一個(gè)新的類(lèi)型
T代表要抽取的對(duì)象 K有一個(gè)約束: 一定是來(lái)自T所有屬性字面量的聯(lián)合類(lèi)型 新的類(lèi)型/屬性一定要從K中選取,
/**
源碼實(shí)現(xiàn)
* From T, pick a set of properties whose keys are in the union K
*/
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
interface PickType {
id: number;
firstName: string;
lastName: string;
}
function showType(args: Pick<PickType, 'firstName' | 'lastName'>) {
console.log(args);
}
showType({ firstName: 'John', lastName: 'Doe' });
// Output: {firstName: "John"}
showType({ id: 3 });
// Error: Object literal may only specify known properties, and 'id' does not exist in type 'Pick<PickType, "firstName" | "lastName">'
Pick 與我們前面討論的工具有一些不同,它需要兩個(gè)參數(shù)
-
T是要從中選擇元素的類(lèi)型
-
K是要選擇的屬性(可以使使用聯(lián)合類(lèi)型來(lái)選擇多個(gè)字段)
Omit
-
Omit<T, K>
Omit的作用與Pick類(lèi)型正好相反。不是選擇元素,而是從類(lèi)型T中刪除K個(gè)屬性。
interface PickType {
id: number;
firstName: string;
lastName: string;
}
function showType(args: Omit<PickType, 'firstName' | 'lastName'>) {
console.log(args);
}
showType({ id: 7 });
// Output: {id: 7}
showType({ firstName: 'John' });
// Error: Object literal may only specify known properties, and 'firstName' does not exist in type 'Pick<PickType, "id">'
Extract
-
Extract<T, U>
提取T中可以賦值給U的類(lèi)型--取交集
Extract允許你通過(guò)選擇兩種不同類(lèi)型中的共有屬性來(lái)構(gòu)造新的類(lèi)型。也就是從T中提取所有可分配給U的屬性。
interface FirstType {
id: number;
firstName: string;
lastName: string;
}
interface SecondType {
id: number;
address: string;
city: string;
}
type ExtractType = Extract<keyof FirstType, keyof SecondType>;
// Output: "id"
在上面的代碼中,FirstType接口和SecondType接口,都存在 id:number屬性。因此,通過(guò)使用Extract,即提取出了新的類(lèi)型 {id:number}。
Exclude
Exclude<T, U> --從 T 中剔除可以賦值給 U 的類(lèi)型。
與Extract不同,Exclude通過(guò)排除兩個(gè)不同類(lèi)型中已經(jīng)存在的共有屬性來(lái)構(gòu)造新的類(lèi)型。它會(huì)從T中排除所有可分配給U的字段。
interface FirstType {
id: number;
firstName: string;
lastName: string;
}
interface SecondType {
id: number;
address: string;
city: string;
}
type ExcludeType = Exclude<keyof FirstType, keyof SecondType>;
// Output; "firstName" | "lastName"
上面的代碼可以看到,屬性firstName和lastName 在SecondType類(lèi)型中不存在。通過(guò)使用Extract關(guān)鍵字,我們可以獲得T中存在而U中不存在的字段。
Record
-
Record<K,T>
此工具可幫助你構(gòu)造具有給定類(lèi)型T的一組屬性K的類(lèi)型。將一個(gè)類(lèi)型的屬性映射到另一個(gè)類(lèi)型的屬性時(shí),Record非常方便。
interface EmployeeType {
id: number;
fullname: string;
role: string;
}
let employees: Record<number, EmployeeType> = {
0: { id: 1, fullname: 'John Doe', role: 'Designer' },
1: { id: 2, fullname: 'Ibrahima Fall', role: 'Developer' },
2: { id: 3, fullname: 'Sara Duckson', role: 'Developer' },
};
// 0: { id: 1, fullname: "John Doe", role: "Designer" },
// 1: { id: 2, fullname: "Ibrahima Fall", role: "Developer" },
// 2: { id: 3, fullname: "Sara Duckson", role: "Developer" }
Record的工作方式相對(duì)簡(jiǎn)單。在代碼中,它期望一個(gè)number作為類(lèi)型,這就是為什么我們將 0、1 和 2 作為employees變量的鍵的原因。如果你嘗試使用字符串作為屬性,則會(huì)引發(fā)錯(cuò)誤,因?yàn)閷傩允怯?code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #28ca71;">EmployeeType給出的具有 ID,fullName 和 role 字段的對(duì)象。
NonNullable
-
NonNullable<T>
-- 從 T 中剔除 null 和 undefined
type NonNullableType = string | number | null | undefined;
function showType(args: NonNullable<NonNullableType>) {
console.log(args);
}
showType('test');
// Output: "test"
showType(1);
// Output: 1
showType(null);
// Error: Argument of type 'null' is not assignable to parameter of type 'string | number'.
showType(undefined);
// Error: Argument of type 'undefined' is not assignable to parameter of type 'string | number'.
我們將類(lèi)型NonNullableType作為參數(shù)傳遞給NonNullable,NonNullable通過(guò)排除null和undefined來(lái)構(gòu)造新類(lèi)型。也就是說(shuō),如果你傳遞可為空的值,TypeScript 將引發(fā)錯(cuò)誤。
順便說(shuō)一句,如果將--strictNullChecks標(biāo)志添加到tsconfig文件,TypeScript 將應(yīng)用非空性規(guī)則。
Mapped Types( 映射類(lèi)型)
映射類(lèi)型允許你從一個(gè)舊的類(lèi)型,生成一個(gè)新的類(lèi)型。
請(qǐng)注意,前面介紹的某些高級(jí)類(lèi)型也是映射類(lèi)型。如:
/*
Readonly, Partial和 Pick是同態(tài)的,但 Record不是。因?yàn)?Record并不需要輸入類(lèi)型來(lái)拷貝屬性,所以它不屬于同態(tài):
*/
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
Record;
type StringMap<T> = {
[P in keyof T]: string;
};
function showType(arg: StringMap<{ id: number; name: string }>) {
console.log(arg);
}
showType({ id: 1, name: 'Test' });
// Error: Type 'number' is not assignable to type 'string'.
showType({ id: 'testId', name: 'This is a Test' });
// Output: {id: "testId", name: "This is a Test"}
StringMap<>會(huì)將傳入的任何類(lèi)型轉(zhuǎn)換為字符串。就是說(shuō),如果我們?cè)诤瘮?shù)showType()中使用它,則接收到的參數(shù)必須是字符串-否則,TypeScript 將引發(fā)錯(cuò)誤。
Type Guards(類(lèi)型保護(hù))
類(lèi)型保護(hù)使你可以使用運(yùn)算符檢查變量或?qū)ο蟮念?lèi)型。這是一個(gè)條件塊,它使用typeof,instanceof或in返回類(lèi)型。
typescript 能夠在特定區(qū)塊中保證變量屬于某種確定類(lèi)型??梢栽诖藚^(qū)塊中放心地引用此類(lèi)型的屬性,或者調(diào)用此類(lèi)型的方法
typeof
function showType(x: number | string) {
if (typeof x === 'number') {
return `The result is ${x + x}`;
}
throw new Error(`This operation can't be done on a ${typeof x}`);
}
showType("I'm not a number");
// Error: This operation can't be done on a string
showType(7);
// Output: The result is 14
什么代碼中,有一個(gè)普通的 JavaScript 條件塊,通過(guò)typeof檢查接收到的參數(shù)的類(lèi)型。
instanceof
class Foo {
bar() {
return 'Hello World';
}
}
class Bar {
baz = '123';
}
function showType(arg: Foo | Bar) {
if (arg instanceof Foo) {
console.log(arg.bar());
return arg.bar();
}
throw new Error('The type is not supported');
}
showType(new Foo());
// Output: Hello World
showType(new Bar());
// Error: The type is not supported
像前面的示例一樣,這也是一個(gè)類(lèi)型保護(hù),它檢查接收到的參數(shù)是否是Foo類(lèi)的一部分,并對(duì)其進(jìn)行處理。
in
interface FirstType {
x: number;
}
interface SecondType {
y: string;
}
function showType(arg: FirstType | SecondType) {
if ('x' in arg) {
console.log(`The property ${arg.x} exists`);
return `The property ${arg.x} exists`;
}
throw new Error('This type is not expected');
}
showType({ x: 7 });
// Output: The property 7 exists
showType({ y: 'ccc' });
// Error: This type is not expected
什么的栗子中,使用in檢查參數(shù)對(duì)象上是否存在屬性x。
Conditional Types(條件類(lèi)型)
條件類(lèi)型測(cè)試兩種類(lèi)型,然后根據(jù)該測(cè)試的結(jié)果選擇其中一種。
一種由條件表達(dá)式所決定的類(lèi)型, 表現(xiàn)形式為 T extends U ? X : Y , 即如果類(lèi)型T可以被賦值給類(lèi)型U,那么結(jié)果類(lèi)型就是X類(lèi)型,否則為Y類(lèi)型。
條件類(lèi)型使類(lèi)型具有了不唯一性,增加了語(yǔ)言的靈活性,
// 源碼實(shí)現(xiàn)
type NonNullable<T> = T extends null | undefined ? never : T;
// NotNull<T> 等價(jià)于 NoneNullable<T,U>
// 用法示例
type resType = NonNullable<string | number | null | undefined>; // string|number
上面的代碼中, NonNullable檢查類(lèi)型是否為 null,并根據(jù)該類(lèi)型進(jìn)行處理。正如你所看到的,它使用了 JavaScript 三元運(yùn)算符。
感謝閱讀。
內(nèi)推社群
我組建了一個(gè)氛圍特別好的騰訊內(nèi)推社群,如果你對(duì)加入騰訊感興趣的話(后續(xù)有計(jì)劃也可以),我們可以一起進(jìn)行面試相關(guān)的答疑、聊聊面試的故事、并且在你準(zhǔn)備好的時(shí)候隨時(shí)幫你內(nèi)推。下方加 winty 好友回復(fù)「面試」即可。
瀏覽
47
點(diǎn)擊上方 前端Q,關(guān)注公眾號(hào)
回復(fù)加群,加入前端Q技術(shù)交流群
原文:https://juejin.cn/post/6904150785966211086
作者:望道
TypeScript 是一種類(lèi)型化的語(yǔ)言,允許你指定變量的類(lèi)型,函數(shù)參數(shù),返回的值和對(duì)象屬性。
你可以把本文看做一個(gè)帶有示例的 TypeScript 高級(jí)類(lèi)型備忘單
讓我們開(kāi)始吧!
Intersection Types(交叉類(lèi)型)
交叉類(lèi)型是一種將多種類(lèi)型組合為一種類(lèi)型的方法。這意味著你可以將給定的類(lèi)型 A 與類(lèi)型 B 或更多類(lèi)型合并,并獲得具有所有屬性的單個(gè)類(lèi)型。
type LeftType = {
id: number;
left: string;
};
type RightType = {
id: number;
right: string;
};
type IntersectionType = LeftType & RightType;
function showType(args: IntersectionType) {
console.log(args);
}
showType({ id: 1, left: 'test', right: 'test' });
// Output: {id: 1, left: "test", right: "test"}
如你所見(jiàn),IntersectionType組合了兩種類(lèi)型-LeftType和RightType,并使用&符號(hào)形成了交叉類(lèi)型。
Union Types(聯(lián)合類(lèi)型)
聯(lián)合類(lèi)型使你可以賦予同一個(gè)變量不同的類(lèi)型
type UnionType = string | number;
function showType(arg: UnionType) {
console.log(arg);
}
showType('test');
// Output: test
showType(7);
// Output: 7
函數(shù)showType是一個(gè)聯(lián)合類(lèi)型函數(shù),它接受字符串或者數(shù)字作為參數(shù)。
Generic Types(泛型)
泛型類(lèi)型是復(fù)用給定類(lèi)型的一部分的一種方式。它有助于捕獲作為參數(shù)傳遞的類(lèi)型 T。
優(yōu)點(diǎn): 創(chuàng)建可重用的函數(shù),一個(gè)函數(shù)可以支持多種類(lèi)型的數(shù)據(jù)。這樣開(kāi)發(fā)者就可以根據(jù)自己的數(shù)據(jù)類(lèi)型來(lái)使用函數(shù)
泛型函數(shù)
function showType<T>(args: T) {
console.log(args);
}
showType('test');
// Output: "test"
showType(1);
// Output: 1
如何創(chuàng)建泛型類(lèi)型:需要使用<>并將 T(名稱(chēng)可自定義)作為參數(shù)傳遞。上面的 ?? 栗子中, 我們給 showType 添加了類(lèi)型變量 T。T幫助我們捕獲用戶傳入的參數(shù)的類(lèi)型(比如:number/string)之后我們就可以使用這個(gè)類(lèi)型
我們把 showType 函數(shù)叫做泛型函數(shù),因?yàn)樗梢赃m用于多個(gè)類(lèi)型
泛型接口
interface GenericType<T> {
id: number;
name: T;
}
function showType(args: GenericType<string>) {
console.log(args);
}
showType({ id: 1, name: 'test' });
// Output: {id: 1, name: "test"}
function showTypeTwo(args: GenericType<number>) {
console.log(args);
}
showTypeTwo({ id: 1, name: 4 });
// Output: {id: 1, name: 4}
在上面的栗子中,聲明了一個(gè) GenericType 接口,該接口接收泛型類(lèi)型 T, 并通過(guò)類(lèi)型 T來(lái)約束接口內(nèi) name 的類(lèi)型
注:泛型變量約束了整個(gè)接口后,在實(shí)現(xiàn)的時(shí)候,必須指定一個(gè)類(lèi)型
因此在使用時(shí)我們可以將name設(shè)置為任意類(lèi)型的值,示例中為字符串或數(shù)字
多參數(shù)的泛型類(lèi)型
interface GenericType<T, U> {
id: T;
name: U;
}
function showType(args: GenericType<number, string>) {
console.log(args);
}
showType({ id: 1, name: 'test' });
// Output: {id: 1, name: "test"}
function showTypeTwo(args: GenericType<string, string[]>) {
console.log(args);
}
showTypeTwo({ id: '001', name: ['This', 'is', 'a', 'Test'] });
// Output: {id: "001", name: Array["This", "is", "a", "Test"]}
泛型類(lèi)型可以接收多個(gè)參數(shù)。在上面的代碼中,我們傳入兩個(gè)參數(shù):T和U,然后將它們用作id,name的類(lèi)型。也就是說(shuō),我們現(xiàn)在可以使用該接口并提供不同的類(lèi)型作為參數(shù)。
Utility Types
TypeScript 內(nèi)部也提供了很多方便實(shí)用的工具,可幫助我們更輕松地操作類(lèi)型。如果要使用它們,你需要將類(lèi)型傳遞給<>
Partial
-
Partial<T>
Partial 允許你將T類(lèi)型的所有屬性設(shè)為可選。它將在每一個(gè)字段后面添加一個(gè)?。
interface PartialType {
id: number;
firstName: string;
lastName: string;
}
/*
等效于
interface PartialType {
id?: number
firstName?: string
lastName?: string
}
*/
function showType(args: Partial<PartialType>) {
console.log(args);
}
showType({ id: 1 });
// Output: {id: 1}
showType({ firstName: 'John', lastName: 'Doe' });
// Output: {firstName: "John", lastName: "Doe"}
上面代碼中聲明了一個(gè)PartialType接口,它用作函數(shù)showType()的參數(shù)的類(lèi)型。為了使所有字段都變?yōu)榭蛇x,我們使用Partial關(guān)鍵字并將PartialType類(lèi)型作為參數(shù)傳遞。
Required
-
Required<T>
將某個(gè)類(lèi)型里的屬性全部變?yōu)楸剡x項(xiàng)
interface RequiredType {
id: number;
firstName?: string;
lastName?: string;
}
function showType(args: Required<RequiredType>) {
console.log(args);
}
showType({ id: 1, firstName: 'John', lastName: 'Doe' });
// Output: { id: 1, firstName: "John", lastName: "Doe" }
showType({ id: 1 });
// Error: Type '{ id: number: }' is missing the following properties from type 'Required<RequiredType>': firstName, lastName
上面的代碼中,即使我們?cè)谑褂媒涌谥跋葘⒛承傩栽O(shè)為可選,但Required被加入后也會(huì)使所有屬性成為必選。如果省略某些必選參數(shù),TypeScript 將報(bào)錯(cuò)。
Readonly
-
Readonly<T>
會(huì)轉(zhuǎn)換類(lèi)型的所有屬性,以使它們無(wú)法被修改
interface ReadonlyType {
id: number;
name: string;
}
function showType(args: Readonly<ReadonlyType>) {
args.id = 4;
console.log(args);
}
showType({ id: 1, name: 'Doe' });
// Error: Cannot assign to 'id' because it is a read-only property.
我們使用Readonly來(lái)使ReadonlyType的屬性不可被修改。也就是說(shuō),如果你嘗試為這些字段之一賦予新值,則會(huì)引發(fā)錯(cuò)誤。
除此之外,你還可以在指定的屬性前面使用關(guān)鍵字readonly使其無(wú)法被重新賦值
interface ReadonlyType {
readonly id: number;
name: string;
}
Pick
-
Pick<T, K>
此方法允許你從一個(gè)已存在的類(lèi)型 T中選擇一些屬性作為K, 從而創(chuàng)建一個(gè)新類(lèi)型
即 抽取一個(gè)類(lèi)型/接口中的一些子集作為一個(gè)新的類(lèi)型
T代表要抽取的對(duì)象 K有一個(gè)約束: 一定是來(lái)自T所有屬性字面量的聯(lián)合類(lèi)型 新的類(lèi)型/屬性一定要從K中選取,
/**
源碼實(shí)現(xiàn)
* From T, pick a set of properties whose keys are in the union K
*/
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
interface PickType {
id: number;
firstName: string;
lastName: string;
}
function showType(args: Pick<PickType, 'firstName' | 'lastName'>) {
console.log(args);
}
showType({ firstName: 'John', lastName: 'Doe' });
// Output: {firstName: "John"}
showType({ id: 3 });
// Error: Object literal may only specify known properties, and 'id' does not exist in type 'Pick<PickType, "firstName" | "lastName">'
Pick 與我們前面討論的工具有一些不同,它需要兩個(gè)參數(shù)
-
T是要從中選擇元素的類(lèi)型 -
K是要選擇的屬性(可以使使用聯(lián)合類(lèi)型來(lái)選擇多個(gè)字段)
Omit
-
Omit<T, K>
Omit的作用與Pick類(lèi)型正好相反。不是選擇元素,而是從類(lèi)型T中刪除K個(gè)屬性。
interface PickType {
id: number;
firstName: string;
lastName: string;
}
function showType(args: Omit<PickType, 'firstName' | 'lastName'>) {
console.log(args);
}
showType({ id: 7 });
// Output: {id: 7}
showType({ firstName: 'John' });
// Error: Object literal may only specify known properties, and 'firstName' does not exist in type 'Pick<PickType, "id">'
Extract
-
Extract<T, U>
提取
T中可以賦值給U的類(lèi)型--取交集
Extract允許你通過(guò)選擇兩種不同類(lèi)型中的共有屬性來(lái)構(gòu)造新的類(lèi)型。也就是從T中提取所有可分配給U的屬性。
interface FirstType {
id: number;
firstName: string;
lastName: string;
}
interface SecondType {
id: number;
address: string;
city: string;
}
type ExtractType = Extract<keyof FirstType, keyof SecondType>;
// Output: "id"
在上面的代碼中,FirstType接口和SecondType接口,都存在 id:number屬性。因此,通過(guò)使用Extract,即提取出了新的類(lèi)型 {id:number}。
Exclude
Exclude<T, U>--從T中剔除可以賦值給U的類(lèi)型。
與Extract不同,Exclude通過(guò)排除兩個(gè)不同類(lèi)型中已經(jīng)存在的共有屬性來(lái)構(gòu)造新的類(lèi)型。它會(huì)從T中排除所有可分配給U的字段。
interface FirstType {
id: number;
firstName: string;
lastName: string;
}
interface SecondType {
id: number;
address: string;
city: string;
}
type ExcludeType = Exclude<keyof FirstType, keyof SecondType>;
// Output; "firstName" | "lastName"
上面的代碼可以看到,屬性firstName和lastName 在SecondType類(lèi)型中不存在。通過(guò)使用Extract關(guān)鍵字,我們可以獲得T中存在而U中不存在的字段。
Record
-
Record<K,T>
此工具可幫助你構(gòu)造具有給定類(lèi)型T的一組屬性K的類(lèi)型。將一個(gè)類(lèi)型的屬性映射到另一個(gè)類(lèi)型的屬性時(shí),Record非常方便。
interface EmployeeType {
id: number;
fullname: string;
role: string;
}
let employees: Record<number, EmployeeType> = {
0: { id: 1, fullname: 'John Doe', role: 'Designer' },
1: { id: 2, fullname: 'Ibrahima Fall', role: 'Developer' },
2: { id: 3, fullname: 'Sara Duckson', role: 'Developer' },
};
// 0: { id: 1, fullname: "John Doe", role: "Designer" },
// 1: { id: 2, fullname: "Ibrahima Fall", role: "Developer" },
// 2: { id: 3, fullname: "Sara Duckson", role: "Developer" }
Record的工作方式相對(duì)簡(jiǎn)單。在代碼中,它期望一個(gè)number作為類(lèi)型,這就是為什么我們將 0、1 和 2 作為employees變量的鍵的原因。如果你嘗試使用字符串作為屬性,則會(huì)引發(fā)錯(cuò)誤,因?yàn)閷傩允怯?code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #28ca71;">EmployeeType給出的具有 ID,fullName 和 role 字段的對(duì)象。
NonNullable
-
NonNullable<T>
-- 從
T中剔除null和undefined
type NonNullableType = string | number | null | undefined;
function showType(args: NonNullable<NonNullableType>) {
console.log(args);
}
showType('test');
// Output: "test"
showType(1);
// Output: 1
showType(null);
// Error: Argument of type 'null' is not assignable to parameter of type 'string | number'.
showType(undefined);
// Error: Argument of type 'undefined' is not assignable to parameter of type 'string | number'.
我們將類(lèi)型NonNullableType作為參數(shù)傳遞給NonNullable,NonNullable通過(guò)排除null和undefined來(lái)構(gòu)造新類(lèi)型。也就是說(shuō),如果你傳遞可為空的值,TypeScript 將引發(fā)錯(cuò)誤。
順便說(shuō)一句,如果將--strictNullChecks標(biāo)志添加到tsconfig文件,TypeScript 將應(yīng)用非空性規(guī)則。
Mapped Types( 映射類(lèi)型)
映射類(lèi)型允許你從一個(gè)舊的類(lèi)型,生成一個(gè)新的類(lèi)型。
請(qǐng)注意,前面介紹的某些高級(jí)類(lèi)型也是映射類(lèi)型。如:
/*
Readonly, Partial和 Pick是同態(tài)的,但 Record不是。因?yàn)?Record并不需要輸入類(lèi)型來(lái)拷貝屬性,所以它不屬于同態(tài):
*/
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
Record;
type StringMap<T> = {
[P in keyof T]: string;
};
function showType(arg: StringMap<{ id: number; name: string }>) {
console.log(arg);
}
showType({ id: 1, name: 'Test' });
// Error: Type 'number' is not assignable to type 'string'.
showType({ id: 'testId', name: 'This is a Test' });
// Output: {id: "testId", name: "This is a Test"}
StringMap<>會(huì)將傳入的任何類(lèi)型轉(zhuǎn)換為字符串。就是說(shuō),如果我們?cè)诤瘮?shù)showType()中使用它,則接收到的參數(shù)必須是字符串-否則,TypeScript 將引發(fā)錯(cuò)誤。
Type Guards(類(lèi)型保護(hù))
類(lèi)型保護(hù)使你可以使用運(yùn)算符檢查變量或?qū)ο蟮念?lèi)型。這是一個(gè)條件塊,它使用typeof,instanceof或in返回類(lèi)型。
typescript 能夠在特定區(qū)塊中保證變量屬于某種確定類(lèi)型??梢栽诖藚^(qū)塊中放心地引用此類(lèi)型的屬性,或者調(diào)用此類(lèi)型的方法
typeof
function showType(x: number | string) {
if (typeof x === 'number') {
return `The result is ${x + x}`;
}
throw new Error(`This operation can't be done on a ${typeof x}`);
}
showType("I'm not a number");
// Error: This operation can't be done on a string
showType(7);
// Output: The result is 14
什么代碼中,有一個(gè)普通的 JavaScript 條件塊,通過(guò)typeof檢查接收到的參數(shù)的類(lèi)型。
instanceof
class Foo {
bar() {
return 'Hello World';
}
}
class Bar {
baz = '123';
}
function showType(arg: Foo | Bar) {
if (arg instanceof Foo) {
console.log(arg.bar());
return arg.bar();
}
throw new Error('The type is not supported');
}
showType(new Foo());
// Output: Hello World
showType(new Bar());
// Error: The type is not supported
像前面的示例一樣,這也是一個(gè)類(lèi)型保護(hù),它檢查接收到的參數(shù)是否是Foo類(lèi)的一部分,并對(duì)其進(jìn)行處理。
in
interface FirstType {
x: number;
}
interface SecondType {
y: string;
}
function showType(arg: FirstType | SecondType) {
if ('x' in arg) {
console.log(`The property ${arg.x} exists`);
return `The property ${arg.x} exists`;
}
throw new Error('This type is not expected');
}
showType({ x: 7 });
// Output: The property 7 exists
showType({ y: 'ccc' });
// Error: This type is not expected
什么的栗子中,使用in檢查參數(shù)對(duì)象上是否存在屬性x。
Conditional Types(條件類(lèi)型)
條件類(lèi)型測(cè)試兩種類(lèi)型,然后根據(jù)該測(cè)試的結(jié)果選擇其中一種。
一種由條件表達(dá)式所決定的類(lèi)型, 表現(xiàn)形式為
T extends U ? X : Y, 即如果類(lèi)型T可以被賦值給類(lèi)型U,那么結(jié)果類(lèi)型就是X類(lèi)型,否則為Y類(lèi)型。條件類(lèi)型使類(lèi)型具有了不唯一性,增加了語(yǔ)言的靈活性,
// 源碼實(shí)現(xiàn)
type NonNullable<T> = T extends null | undefined ? never : T;
// NotNull<T> 等價(jià)于 NoneNullable<T,U>
// 用法示例
type resType = NonNullable<string | number | null | undefined>; // string|number
上面的代碼中, NonNullable檢查類(lèi)型是否為 null,并根據(jù)該類(lèi)型進(jìn)行處理。正如你所看到的,它使用了 JavaScript 三元運(yùn)算符。
感謝閱讀。
內(nèi)推社群
我組建了一個(gè)氛圍特別好的騰訊內(nèi)推社群,如果你對(duì)加入騰訊感興趣的話(后續(xù)有計(jì)劃也可以),我們可以一起進(jìn)行面試相關(guān)的答疑、聊聊面試的故事、并且在你準(zhǔn)備好的時(shí)候隨時(shí)幫你內(nèi)推。下方加 winty 好友回復(fù)「面試」即可。
