
TypeScript 是一種類型化的語言,允許你指定變量的類型,函數(shù)參數(shù),返回的值和對象屬性。
你可以把本文看做一個(gè)帶有示例的 TypeScript 高級類型備忘單
讓我們開始吧!
Intersection Types(交叉類型)
Union Types(聯(lián)合類型)
Generic Types(泛型)
泛型函數(shù)
泛型接口
多參數(shù)的泛型類型
Utility Types
Partial
Required
Readonly
Pick
Omit
Extract
Exclude
Record
NonNullable
Mapped Types( 映射類型)
Type Guards(類型保護(hù))
typeof
instanceof
in
Conditional Types(條件類型)
Intersection Types(交叉類型)
交叉類型是一種將多種類型組合為一種類型的方法。 這意味著你可以將給定的類型 A 與類型 B 或更多類型合并,并獲得具有所有屬性的單個(gè)類型。
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"}
如你所見,IntersectionType組合了兩種類型-LeftType和RightType,并使用&符號形成了交叉類型。
Union Types(聯(lián)合類型)
聯(lián)合類型使你可以賦予同一個(gè)變量不同的類型
type UnionType = string | number;
function showType(arg: UnionType) {
? ? console.log(arg);
}
showType('test');
// Output: test
showType(7);
// Output: 7
函數(shù)showType是一個(gè)聯(lián)合類型函數(shù),它接受字符串或者數(shù)字作為參數(shù)。
Generic Types(泛型)
泛型類型是復(fù)用給定類型的一部分的一種方式。 它有助于捕獲作為參數(shù)傳遞的類型 T。
優(yōu)點(diǎn): 創(chuàng)建可重用的函數(shù),一個(gè)函數(shù)可以支持多種類型的數(shù)據(jù)。 這樣開發(fā)者就可以根據(jù)自己的數(shù)據(jù)類型來使用函數(shù)
泛型函數(shù)
function showType<T>(args: T) {
? ? console.log(args);
}
showType('test');
// Output: "test"
showType(1);
// Output: 1
如何創(chuàng)建泛型類型:需要使用<>并將 T(名稱可自定義)作為參數(shù)傳遞。 上面的? 栗子中, 我們給 showType 添加了類型變量 T。T幫助我們捕獲用戶傳入的參數(shù)的類型(比如:number/string)之后我們就可以使用這個(gè)類型
我們把 showType 函數(shù)叫做泛型函數(shù),因?yàn)樗梢赃m用于多個(gè)類型
泛型接口
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 接口,該接口接收泛型類型 T, 并通過類型 T來約束接口內(nèi) name 的類型
注:泛型變量約束了整個(gè)接口后,在實(shí)現(xiàn)的時(shí)候,必須指定一個(gè)類型
因此在使用時(shí)我們可以將name設(shè)置為任意類型的值,示例中為字符串或數(shù)字
多參數(shù)的泛型類型
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"]}
泛型類型可以接收多個(gè)參數(shù)。 在上面的代碼中,我們傳入兩個(gè)參數(shù):T和U,然后將它們用作id,name的類型。 也就是說,我們現(xiàn)在可以使用該接口并提供不同的類型作為參數(shù)。
Utility Types
TypeScript 內(nèi)部也提供了很多方便實(shí)用的工具,可幫助我們更輕松地操作類型。 如果要使用它們,你需要將類型傳遞給<>
Partial
Partial<T>
Partial 允許你將T類型的所有屬性設(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ù)的類型。 為了使所有字段都變?yōu)榭蛇x,我們使用Partial關(guān)鍵字并將PartialType類型作為參數(shù)傳遞。
Required
Required<T>
將某個(gè)類型里的屬性全部變?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
上面的代碼中,即使我們在使用接口之前先將某些屬性設(shè)為可選,但Required被加入后也會使所有屬性成為必選。 如果省略某些必選參數(shù),TypeScript 將報(bào)錯(cuò)。
Readonly
Readonly<T>
會轉(zhuǎn)換類型的所有屬性,以使它們無法被修改
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來使ReadonlyType的屬性不可被修改。 也就是說,如果你嘗試為這些字段之一賦予新值,則會引發(fā)錯(cuò)誤。
除此之外,你還可以在指定的屬性前面使用關(guān)鍵字readonly使其無法被重新賦值
interface ReadonlyType {
? ? readonly id: number;
? ? name: string;
}
Pick
Pick<T, K>
此方法允許你從一個(gè)已存在的類型 T中選擇一些屬性作為K, 從而創(chuàng)建一個(gè)新類型
即 抽取一個(gè)類型/接口中的一些子集作為一個(gè)新的類型
T代表要抽取的對象 K有一個(gè)約束: 一定是來自T所有屬性字面量的聯(lián)合類型 新的類型/屬性一定要從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是要從中選擇元素的類型
K是要選擇的屬性(可以使使用聯(lián)合類型來選擇多個(gè)字段)
Omit
Omit<T, K>
Omit的作用與Pick類型正好相反。 不是選擇元素,而是從類型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的類型--取交集
Extract允許你通過選擇兩種不同類型中的共有屬性來構(gòu)造新的類型。 也就是從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"
在上面的代碼中,F(xiàn)irstType接口和SecondType接口,都存在 id:number屬性。 因此,通過使用Extract,即提取出了新的類型 {id:number}。
Exclude
Exclude<T, U> --從 T 中剔除可以賦值給 U 的類型。
與Extract不同,Exclude通過排除兩個(gè)不同類型中已經(jīng)存在的共有屬性來構(gòu)造新的類型。 它會從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類型中不存在。 通過使用Extract關(guān)鍵字,我們可以獲得T中存在而U中不存在的字段。
Record
Record<K,T>
此工具可幫助你構(gòu)造具有給定類型T的一組屬性K的類型。將一個(gè)類型的屬性映射到另一個(gè)類型的屬性時(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的工作方式相對簡單。 在代碼中,它期望一個(gè)number作為類型,這就是為什么我們將 0、1 和 2 作為employees變量的鍵的原因。 如果你嘗試使用字符串作為屬性,則會引發(fā)錯(cuò)誤,因?yàn)閷傩允怯蒃mployeeType給出的具有 ID,fullName 和 role 字段的對象。
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'.
我們將類型NonNullableType作為參數(shù)傳遞給NonNullable,NonNullable通過排除null和undefined來構(gòu)造新類型。 也就是說,如果你傳遞可為空的值,TypeScript 將引發(fā)錯(cuò)誤。
順便說一句,如果將--strictNullChecks標(biāo)志添加到tsconfig文件,TypeScript 將應(yīng)用非空性規(guī)則。
Mapped Types( 映射類型)
映射類型允許你從一個(gè)舊的類型,生成一個(gè)新的類型。
請注意,前面介紹的某些高級類型也是映射類型。 如:
/*
Readonly, Partial和 Pick是同態(tài)的,但 Record不是。 因?yàn)?Record并不需要輸入類型來拷貝屬性,所以它不屬于同態(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<>會將傳入的任何類型轉(zhuǎn)換為字符串。 就是說,如果我們在函數(shù)showType()中使用它,則接收到的參數(shù)必須是字符串-否則,TypeScript 將引發(fā)錯(cuò)誤。
Type Guards(類型保護(hù))
類型保護(hù)使你可以使用運(yùn)算符檢查變量或?qū)ο蟮念愋汀?這是一個(gè)條件塊,它使用typeof,instanceof或in返回類型。
typescript 能夠在特定區(qū)塊中保證變量屬于某種確定類型??梢栽诖藚^(qū)塊中放心地引用此類型的屬性,或者調(diào)用此類型的方法
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 條件塊,通過typeof檢查接收到的參數(shù)的類型。
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è)類型保護(hù),它檢查接收到的參數(shù)是否是Foo類的一部分,并對其進(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ù)對象上是否存在屬性x。
Conditional Types(條件類型)
條件類型測試兩種類型,然后根據(jù)該測試的結(jié)果選擇其中一種。
一種由條件表達(dá)式所決定的類型, 表現(xiàn)形式為 T extends U ? X : Y , 即如果類型T可以被賦值給類型U,那么結(jié)果類型就是X類型,否則為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檢查類型是否為 null,并根據(jù)該類型進(jìn)行處理。 正如你所看到的,它使用了 JavaScript 三元運(yùn)算符。
恭喜您讀到這里,如果想了解更多技術(shù),可以訪問我的網(wǎng)站:路條編程。