交叉类型
交叉类型是将多个类型合并为一个类型。可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。使用符号 & 表示。交叉类型 A & B 表示,任何一个新类型必须同时属于 A 和 B,才属于交叉类型 A & B
- 基础介绍
type Dog = { breed: string; run(): void }; type Bird = { wingspan: number; fly(): void }; type DogAndBird = Dog & Bird; const animal: DogAndBird = { breed: "Labrador", run() { console.log("Dog is running."); }, wingspan: 20, fly() { console.log("Bird is flying."); }, }; console.log(animal.breed); // Labrador animal.run(); // 执行方法打印 Dog is running. animal.fly(); // 执行方法打印 Bird is flying. // animal 同时具有Dog 和 Bird 的特性
- 函数中的应用
通过 extend 把两个对象合并成一个对象,看一下如何通过函数参数类型交叉实现。function extend<T, U>(first: T, second: U): T & U { const result = {} as T & U; console.log(first, second); // { name: '迪西', className: '三年级' } { age: 9, className: '二年级' } for (const key in first) { (result as T)[key] = first[key]; } for (const key in second) { if (!result.hasOwnProperty(key)) { // 检查result是否包含second中的key (result as U)[key] = second[key]; } } return result; } const extendedObject = extend({ name: '迪西', className: '三年级' }, { age: 9, className: '二年级' }); console.log(extendedObject); // {name: '迪西', className: '三年级', age: 9}
- 对象合并
type Car = { brand: string; speed: number }; type Boat = { color: string; sails: number }; type Vehicle = Car & Boat; const hybridVehicle: Vehicle = { brand: "cat", speed: 120, color: "Blue", sails: 2, };
- 可选属性的处理
交叉之后创建的新类型,同样包含原类型中的可选属性type Car = { brand: string; speed?: number }; type Boat = { color: string; sails?: number }; type Vehicle = Car & Boat; const hybridVehicle: Vehicle = { brand: "cat", speed: 120, color: "Blue", };
- 函数重载
函数重载是 TypeScript 中处理不同参数组合的一种方式function merge<T, U>(arg1: T, arg2: U): T & U; function merge<T, U, V>(arg1: T, arg2: U, arg3: V): T & U & V; function merge(...args: any[]): any { return Object.assign({}, ...args); } const mergedObject = merge({ A: 1 }, { B: 2 }, { C: 3 }); console.log(mergedObject); // {A: 1, B: 2, C: 3}
- 类型守卫与交叉类型
通过 in 关键字使用类型守卫检查对象的属性type Car = { brand: string; speed: number }; type Flying = { altitude: number }; function moveVehicle(vehicle: Car & Flying) { if ("speed" in vehicle) { console.log(`speed: ${vehicle.speed}`); // speed: 120 } if ("altitude" in vehicle) { console.log(`Flying: ${vehicle.altitude}`); // Flying: 5000 } } // 使用交叉类型的函数 let flyingCar: Car & Flying = { brand: "SkyCar", speed: 120, altitude: 5000 }; moveVehicle(flyingCar);
联合类型
联合类型是 TypeScript 中非常灵活的一部分,它允许一个值属于多个类型之一
联合类型允许一个值可以是多种类型之一,使用符号 | 表示。
let userName: string | number;
userName = "迪西";
console.log(userName.length); // 2
userName = 9;
console.log(userName, userName.length); // 报错
- 函数返回值
联合类型在函数参数中,允许参数接受多种类型的值,处理不同类型的返回值。function getRandomValue(): string | number { const random = Math.random(); return random > 0.5 ? "Hello" : 42; } const resultNum = getRandomValue(); console.log(resultNum);
- 对象的联合类型
在 TypeScript 中,可以使用联合类型表示具有不同属性的对象。type Square = { kind: "square"; width: number }; type Circle = { kind: "circle"; radius: number }; type Shape = Square | Circle; function area(shape: Shape): number { if (shape.kind === "square") { return shape.width ** 2; } else { return Math.PI * shape.radius ** 2; } } const square: Square = { kind: "square", width: 6 }; console.log(area(square)); // 36 const circle: Circle = { kind: "circle", radius: 4 }; console.log(area(circle)); // 50.26548245743669
- null 与 undefined
联合类型经常与 null 或 undefined 结合使用,以表示变量可能具有空值。type Status = "success" | "error" | "pending" | null | undefined; // Status 可以是 success,error,pending, null,undefined let currentStatus: Status = "success"; console.log(currentStatus);// success currentStatus = null; console.log(currentStatus); // null currentStatus = undefined; console.log(currentStatus); // undefined
- 类型守卫
联合类型与类型守卫结合使用,可以更安全地操作对象。function printMessage(message: string | string[] | null) { if (message instanceof Array) { console.log(message.join(",")); } else if (message === null) { console.log("No message"); } else { console.log(message); } } printMessage("Hello, TypeScript!"); printMessage(["TypeScript", "is", "awesome"]); printMessage(null); // 打印结果 // Hello, TypeScript! // TypeScript,is,awesome // No message
- 可辨识联合
可辨识联合是一种特殊的联合类型,结合了具有共同字段的对象。type SuccessResponse = { status: "1"; data: string }; type ErrorResponse = { status: "2"; error: string }; type ApiResponse = SuccessResponse | ErrorResponse; function handleApiResponse(response: ApiResponse) { switch (response.status) { case "1": console.log(response.data); break; case "2": console.log(response.error); break; } } const successResponse: SuccessResponse = { status: "1", data: "Success" }; const errorResponse: ErrorResponse = { status: "2", error: "Error" }; handleApiResponse(successResponse); handleApiResponse(errorResponse); // 打印结果 // Success // Error
映射类型
映射类型:从旧类型中创建新类型的一种方式 。 在映射类型里,新类型以相同的形式去转换旧类型里每个属性。
它的语法与索引签名的语法类型,内部使用了 for … in。 具有三个部分:
type Keys = 'option1' | 'option2';
type Flags = { [K in Keys]: boolean };
等同于
type Flags = {
option1: boolean;
option2: boolean;
}
类型变量 K,它会依次绑定到每个属性。
字符串字面量联合的 Keys,它包含了要迭代的属性名的集合。
属性的结果类型。
keyof和索引访问类型
type Nullable<T> = { [P in keyof T]: T[P] | null }
type Partial<T> = { [P in keyof T]?: T[P] }
// 属性列表是 keyof T且结果类型是 T[P]的变体。
因为这类转换是 同态的,映射只作用于 T的属性而没有其它的
-
Extract 映射类型
Extract 映射类型允许我们从联合类型中提取符合指定条件的子集。 一般用于处理联合类型// Extract<T, U> -- 提取T中可以赋值给U的类型。 // Extract实现源码 原理很简单 type Extract<T, U> = T extends U ? T : never;
type Test1 = '1' | '2' | '3' const obj: Extract<Test1, '1' | '2'> = '1'; console.log(obj) // 1 const obj2: Extract<Test1, '1' | '2'> = '2'; console.log(obj2) // 2 const obj3: Extract<Test1, '1' | '2'> = '3'; // 1,2 OK 赋值3就会error
-
Exclude 映射类型
Exclude 映射类型允许我们从联合类型中排除符合指定条件的子集。
和 Extract 正好相反,也是用于处理联合类型// Exclude源码 type Exclude<T, U> = T extends U ? never : T;
type Test1 = '1' | '2' | '3' const obj: Exclude<Test1, '1' | '2'> = '3'; // 1,2 OK 赋值3就会error console.log(obj) // 3 // const obj1: Exclude<Test1, '1' | '2'> = '1'; // console.log(obj1) // 1
-
Pick 映射类型
Pick 映射类型允许我们从现有类型中挑选出指定的属性。
可以采集 已定义对象中 自己需要的一部分形成新的定义类型。// Pick 的源码 type Pick<T, K extends keyof T> = { [P in K]: T[P]; };
interface UserObj { readonly name: string; age: number; id: number; sex: 0 | 1; address: string; weight: number; } // 采集需要的 type Person = Pick<UserObj, "name" | "id">; // 此时Person 等同于 Person1 //interface Person1 { // readonly name: string; // id: number; //} const obj: Person = {name: '迪西', 'id': 1}; // const obj: Person = {name: '迪西', 'id': 1, sex: 0}; // sex字段报错
-
Omit 映射类型
可以剔除 已定义对象中 自己不需要的一部分形成新的定义类型。// Omit 的源码 type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
interface UserObj { readonly name: string; // readonly 只读属性 只能初始化定义 不能二次赋值 age: number; id: number; sex: 0 | 1; address: string; weight: number; } // 剔除省略自己不需要的 type Person = Omit<UserObj, "age" | "sex" | "address" | "weight">; // 此时Person 等同于 Person1 // interface Person1 { // readonly name: string; // id: number; // } let obj:Person = {name: '迪西', 'id': 1} // let obj:Person = {name: '迪西', 'id': 1, sex: 0}
-
NonNullable 映射类型
NonNullable 映射类型允许我们从类型中排除 null 和 undefined。// 源码 /** * Exclude null and undefined from T */ type NonNullable<T> = T & {}; // 源码的这一句写的很有意思,泛型参数T和{}的交集就默认排除了`null` 和 `undefined`
type MaybeString = string | null | undefined; let string = 'hello 迪西!' const value: MaybeString = getSomeStringValue(string); // 假设这个函数可能返回一个字符串、null 或 undefined console.log(value); // 使用 NonNullable 确保 value 不会是 null 或 undefined const nonNullableValue: NonNullable<MaybeString> = value!; // 使用 ! 断言操作符,或者通过其他方式确保值不为 null 或 undefined // 现在,nonNullableValue 的类型是 string,因为我们已经排除了 null 和 undefined console.log(nonNullableValue.length); // 9 // 其实就是某些场景绝对为了排除null,undefined的类型才用的 function getSomeStringValue(str) { return str }
-
ReturnType 映射类型 (函数的类型推导返回)
ReturnType 映射类型允许我们获取函数类型的返回值类型。// ReturnType源码 /** * Obtain the return type of a function type */ type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
const myFun = () => ({ name: 'zhangsan', age: 233, sex: '1', tel: 123456789, fun: () => { return 233; }, arr: [1,2,3,4,5,6] }); type Test2 = ReturnType<typeof myFun>; // 错误用法 // const someValue = 42; // type InvalidReturnType = ReturnType<typeof someValue>; // error错误!someValue 不是一个函数。
-
Partial
可把定义好的对象(包含 必选+可选项)类型全部转化为可选项// Partial源码 type Partial<T> = { [P in keyof T]?: T[P]; };
interface Person { name: string; age: number; id: number; sex: 0 | 1; address: string; weight: number; } // 使用方法 const newObj: Partial<Person> = { name: '迪西' // 假如只需要一项 Partial的便捷性 可以不需要从新定义类型 }; // Partial<Person>等同于 NewPerson interface NewPerson { name?: string; age?: number; id?: number; sex?: 0 | 1; address?: string; weight?: number; }
-
Partial
Required 和 Partial刚好相反,可把定义好的对象(包含 必选+可选项)类型全部转化为 必选项// Partial “-?” 意思是移除可选属性 /** * Make all properties in T required */ type Required<T> = { [P in keyof T]-?: T[P]; };
// 已有定义类型Person interface Person { name: string; age: number; id?: number; sex?: 0 | 1; } // 使用方法 const newObj: Required<Person> = { name: '迪西', age: 9, id: 1, sex: 1 }; // Required<Person>等同于 NewPerson interface NewPerson { name: string; age: number; id: number; sex: 0 | 1; }
-
Readonly (转化只读)
Readonly 就是为类型对象每一项添加前缀 Readonly// Readonly 源码 /** * Make all properties in T readonly */ type Readonly<T> = { readonly [P in keyof T]: T[P]; };
interface Person { readonly name: string; // 只有这一项有readonly age: number; id?: number; } // 使用方法 const newObj: Readonly<Person> = { name: '迪西', age: 9, id: 1 }; // newObj.name = '李四'; // 异常 因为有readonly只读属性,只能初始化声明,不能赋值。 // Readonly<Person> 等同于 NewPerson interface NewPerson { readonly name: string; readonly age: number; readonly id?: number; }