当前位置:  首页>> 技术小册>> TypeScript和Vue从入门到精通(一)

2.3 TypeScript中有关类型的高级内容

在TypeScript的世界里,类型系统是其核心特性之一,它不仅帮助开发者在编码阶段捕获潜在的错误,还促进了代码的可读性和可维护性。本章节将深入探讨TypeScript中类型的高级内容,涵盖交叉类型、联合类型、条件类型、映射类型、泛型以及类型断言与类型守卫等关键概念,帮助读者从入门迈向精通。

2.3.1 交叉类型(Intersection Types)

交叉类型是将多个类型合并为一个类型的方式。当你想要一个对象同时拥有多个类型的属性时,交叉类型就显得尤为重要。它允许你将多个类型通过&符号组合起来,形成一个新的类型。

  1. interface IPerson {
  2. name: string;
  3. age: number;
  4. }
  5. interface IEmployee {
  6. id: number;
  7. department: string;
  8. }
  9. type Employee = IPerson & IEmployee;
  10. const employee: Employee = {
  11. name: "Alice",
  12. age: 30,
  13. id: 1,
  14. department: "Engineering"
  15. };

在上面的例子中,Employee类型包含了IPersonIEmployee两个接口的所有属性。

2.3.2 联合类型(Union Types)

与交叉类型相对,联合类型允许一个变量在几种类型之间变化。使用|符号定义联合类型,这意呀着变量可以是这些类型中的任何一个。

  1. let greeting: string | number;
  2. greeting = "Hello, world!";
  3. greeting = 42; // 合法,因为greeting可以是string或number
  4. // 使用类型守卫处理联合类型
  5. function printGreeting(greeting: string | number): void {
  6. if (typeof greeting === "string") {
  7. console.log(greeting.toUpperCase());
  8. } else {
  9. console.log(greeting.toFixed(2)); // 将数字格式化为两位小数
  10. }
  11. }

联合类型在处理多种可能类型的变量时非常有用,但需要谨慎处理,因为直接访问未确定类型的属性或方法可能会导致编译错误。

2.3.3 条件类型(Conditional Types)

条件类型允许TypeScript根据条件编译不同的类型。它们基于三元运算符T extends U ? X : Y的结构,其中T是要比较的类型,U是基准类型,如果T可以赋值给U,则结果为X,否则为Y

  1. type IsString<T> = T extends string ? true : false;
  2. type Result1 = IsString<string>; // true
  3. type Result2 = IsString<number>; // false
  4. // 示例:基于条件类型定义一个函数,该函数参数为对象时返回其属性类型,否则返回void
  5. type ReturnType<T> = T extends object ? keyof T : void;
  6. type ObjKeys = ReturnType<{ a: number, b: string }>; // "a" | "b"
  7. type NonObj = ReturnType<number>; // void

条件类型在构建复杂类型系统时特别有用,如泛型约束、高级类型推断等场景。

2.3.4 映射类型(Mapped Types)

映射类型允许你基于一个已存在的类型创建一个新类型,通过遍历已存在类型的所有属性,并应用某种转换来生成新类型的属性。使用{[P in keyof T]: X}的语法,其中T是源类型,PT的属性名,X是基于T[P]计算得到的新类型。

  1. type Readonly<T> = {
  2. readonly [P in keyof T]: T[P];
  3. };
  4. type Original = {
  5. a: number;
  6. b: string;
  7. };
  8. type ReadOnlyOriginal = Readonly<Original>;
  9. const obj: ReadOnlyOriginal = {
  10. a: 1,
  11. b: "hello"
  12. };
  13. // obj.a = 2; // 编译错误,因为a是readonly

映射类型在创建工具类型时非常有用,如Partial<T>Readonly<T>Pick<T, K>等,这些都是TypeScript标准库中的类型。

2.3.5 泛型(Generics)

泛型是TypeScript中一个强大的特性,它允许你定义组件时不必明确指定具体的类型,而是在使用组件时再指定。这提高了代码的复用性和灵活性。

  1. function identity<T>(arg: T): T {
  2. return arg;
  3. }
  4. let output = identity<string>("myString");
  5. let outputNum = identity(42); // 自动推断为number
  6. // 泛型接口
  7. interface GenericIdentityFn<T> {
  8. (arg: T): T;
  9. }
  10. let myIdentity: GenericIdentityFn<number> = identity;

泛型不仅限于函数和接口,还可以应用于类、类型别名等。它们极大地增强了TypeScript的类型系统的表达力和灵活性。

2.3.6 类型断言与类型守卫

类型断言和类型守卫是处理TypeScript中类型不确定性的两种主要方式。

  • 类型断言:通过<Type>valuevalue as Type的形式直接告诉TypeScript:“我知道这个值的类型是什么,请相信我。”这不会进行任何类型检查。
  1. let someValue: any = "this is a string";
  2. let strLength: number = (<string>someValue).length;
  3. // 或者
  4. let strLengthAs: number = (someValue as string).length;
  • 类型守卫:通过一些表达式来检测值是否属于特定类型,如果是,则TypeScript编译器会在后续的代码块中自动将该值视为该类型。这通常通过函数实现,该函数返回一个布尔值,表示传入的参数是否为期望的类型。
  1. function isString(value: any): value is string {
  2. return typeof value === "string";
  3. }
  4. function doSomething(x: any) {
  5. if (isString(x)) {
  6. console.log(x.toUpperCase()); // 这里x被推断为string
  7. }
  8. }

类型断言和类型守卫各有用途,类型断言更适合你确切知道值类型但TypeScript编译器无法推断的情况,而类型守卫则更适合在运行时检查类型。

总结

本章节深入探讨了TypeScript中类型的高级内容,包括交叉类型、联合类型、条件类型、映射类型、泛型以及类型断言与类型守卫。这些概念是TypeScript类型系统的重要组成部分,掌握它们将帮助你编写更加健壮、可维护的TypeScript代码。通过灵活运用这些高级类型特性,你可以构建出既灵活又安全的类型系统,为你的应用提供坚实的类型保障。


该分类下的相关小册推荐: