TypeScript高级类型系统:构建类型安全的代码

4 阅读4分钟

引言

TypeScript作为JavaScript的超集,不仅提供了静态类型检查,还拥有一个强大而灵活的类型系统。掌握高级类型特性,能够让我们编写出更加类型安全、可维护性更高的代码。本文将深入探讨TypeScript的8大高级类型特性,帮助你充分发挥类型系统的威力。

泛型类型

1. 基础泛型

泛型是TypeScript类型系统的核心,它允许我们编写可复用的组件和函数。

// 泛型函数
function identity<T>(arg: T): T {
  return arg;
}

// 使用示例
const num = identity<number>(42);
const str = identity<string>('hello');

// 类型推断
const inferred = identity('world'); // 自动推断为string

2. 泛型约束

限制泛型的类型范围,确保类型安全。

// 约束泛型必须有length属性
interface Lengthwise {
  length: number;
}

function logLength<T extends Lengthwise>(arg: T): number {
  console.log(arg.length);
  return arg.length;
}

logLength('hello'); // 5
logLength([1, 2, 3]); // 3
logLength({ length: 10, value: 'test' }); // 10

3. 泛型接口和类

// 泛型接口
interface Box<T> {
  value: T;
  getValue(): T;
  setValue(value: T): void;
}

// 泛型类
class Storage<T> implements Box<T> {
  private value: T;

  constructor(initialValue: T) {
    this.value = initialValue;
  }

  getValue(): T {
    return this.value;
  }

  setValue(value: T): void {
    this.value = value;
  }
}

// 使用示例
const stringStorage = new Storage<string>('initial');
const numberStorage = new Storage<number>(100);

条件类型

4. 基础条件类型

条件类型类似于三元表达式,根据类型条件选择不同的类型。

// 基础条件类型
type IsString<T> = T extends string ? true : false;

type Test1 = IsString<string>; // true
type Test2 = IsString<number>; // false

// 实用示例:提取函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function getUser(): string {
  return 'user';
}

type UserReturn = ReturnType<typeof getUser>; // string

5. 分布式条件类型

当条件类型作用于联合类型时,会自动分发到每个成员。

type ToArray<T> = T extends any ? T[] : never;

type StrArrOrNumArr = ToArray<string | number>;
// 等同于 string[] | number[]

// 过滤联合类型
type Filter<T, U> = T extends U ? T : never;

type Strings = Filter<string | number | boolean, string>;
// 等同于 string

映射类型

6. 基础映射类型

映射类型可以基于旧类型创建新类型。

type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

type Partial<T> = {
  [P in keyof T]?: T[P];
};

interface User {
  id: number;
  name: string;
  email: string;
}

type ReadonlyUser = Readonly<User>;
type PartialUser = Partial<User>;

// 使用示例
const user: ReadonlyUser = {
  id: 1,
  name: 'John',
  email: 'john@example.com'
};

// user.id = 2; // 错误:只读属性

7. 高级映射类型

// 可选转必选
type Required<T> = {
  [P in keyof T]-?: T[P];
};

// 只读转可写
type Mutable<T> = {
  -readonly [P in keyof T]: T[P];
};

// 提取特定类型的属性
type PickByType<T, U> = {
  [P in keyof T as T[P] extends U ? P : never]: T[P];
};

interface Product {
  id: number;
  name: string;
  price: number;
  description: string;
}

type StringProperties = PickByType<Product, string>;
// { name: string; description: string }

模板字面量类型

8. 字符串操作类型

TypeScript 4.1引入了模板字面量类型,可以对字符串类型进行操作。

// 基础模板字面量类型
type Greeting = 'hello' | 'hi';
type Target = 'world' | 'everyone';

type Message = `${Greeting} ${Target}`;
// 'hello world' | 'hello everyone' | 'hi world' | 'hi everyone'

// 实用示例:事件名称类型
type EventName<T extends string> = `on${Capitalize<T>}`;

type ClickEvent = EventName<'click'>; // 'onClick'
type SubmitEvent = EventName<'submit'>; // 'onSubmit'

9. 字符串工具类型

// 类型转换
type Uppercase<T extends string> = intrinsic;
type Lowercase<T extends string> = intrinsic;
type Capitalize<T extends string> = intrinsic;
type Uncapitalize<T extends string> = intrinsic;

// 使用示例
type Upper = Uppercase<'hello'>; // 'HELLO'
type Lower = Lowercase<'WORLD'>; // 'world'
type Cap = Capitalize<'hello'>; // 'Hello'
type Uncap = Uncapitalize<'Hello'>; // 'hello'

索引访问类型

10. 深度类型访问

使用索引访问类型可以深入访问嵌套的类型结构。

interface Config {
  database: {
    host: string;
    port: number;
    credentials: {
      username: string;
      password: string;
    };
  };
}

// 访问嵌套类型
type DbConfig = Config['database'];
type HostType = Config['database']['host']; // string
type Credentials = Config['database']['credentials'];

// 实用示例:获取数组元素类型
type ArrayElement<T extends readonly any[]> = T extends readonly (infer U)[] ? U : never;

type Numbers = number[];
type Num = ArrayElement<Numbers>; // number

递归类型

11. 递归类型定义

递归类型可以定义自引用的数据结构。

// 链表节点
type ListNode<T> = {
  value: T;
  next: ListNode<T> | null;
};

// 二叉树节点
type TreeNode<T> = {
  value: T;
  left: TreeNode<T> | null;
  right: TreeNode<T> | null;
};

// 使用示例
const numberNode: ListNode<number> = {
  value: 1,
  next: {
    value: 2,
    next: null
  }
};

12. 递归类型工具

// 深度只读
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};

// 深度可选
type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};

interface NestedConfig {
  level1: {
    level2: {
      value: string;
    };
  };
}

type ReadonlyNested = DeepReadonly<NestedConfig>;

类型推断与守卫

13. 类型推断

使用infer关键字在条件类型中推断类型。

// 推解数组类型
type UnpackedArray<T> = T extends (infer U)[] ? U : T;

type Arr = number[];
type Item = UnpackedArray<Arr>; // number

// 推解Promise类型
type UnpackedPromise<T> = T extends Promise<infer U> ? U : T;

type PromiseString = Promise<string>;
type String = UnpackedPromise<PromiseString>; // string

// 推解函数参数类型
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

function greet(name: string, age: number): void {
  console.log(`Hello ${name}, you are ${age}`);
}

type GreetParams = Parameters<typeof greet>; // [string, number]

14. 类型守卫

类型守卫可以在运行时缩小类型范围。

// typeof类型守卫
function processValue(value: string | number) {
  if (typeof value === 'string') {
    console.log(value.toUpperCase()); // value是string
  } else {
    console.log(value.toFixed(2)); // value是number
  }
}

// instanceof类型守卫
class Dog {
  bark() { console.log('Woof!'); }
}

class Cat {
  meow() { console.log('Meow!'); }
}

function makeSound(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    animal.bark();
  } else {
    animal.meow();
  }
}

// 自定义类型守卫
interface Bird {
  fly(): void;
  layEggs(): void;
}

interface Fish {
  swim(): void;
  layEggs(): void;
}

function isFish(pet: Bird | Fish): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

function move(pet: Bird | Fish) {
  if (isFish(pet)) {
    pet.swim();
  } else {
    pet.fly();
  }
}

实用类型组合

15. 构建复杂类型

组合多种类型特性构建实用的复杂类型。

// 不可变状态类型
type ImmutableState<T> = DeepReadonly<{
  [K in keyof T]: T[K];
}>;

// API响应类型
type ApiResponse<T, E = Error> = {
  success: boolean;
  data?: T;
  error?: E;
};

// 分页数据类型
type PaginatedData<T> = {
  items: T[];
  total: number;
  page: number;
  pageSize: number;
  hasMore: boolean;
};

// 使用示例
interface User {
  id: number;
  name: string;
  email: string;
}

type UserResponse = ApiResponse<User>;
type UserListResponse = ApiResponse<PaginatedData<User>>;

// 函数重载类型
type Overloaded<T> = {
  (value: string): T;
  (value: number): T;
  (value: boolean): T;
};

// 工厂函数类型
type Factory<T> = new (...args: any[]) => T;

class Product {
  constructor(public name: string) {}
}

type ProductFactory = Factory<Product>;

总结

TypeScript的高级类型系统为我们提供了强大的类型安全能力:

核心特性

  1. 泛型:编写可复用的类型安全代码
  2. 条件类型:根据类型条件动态选择类型
  3. 映射类型:基于现有类型创建新类型
  4. 模板字面量类型:操作字符串类型
  5. 递归类型:定义复杂的数据结构
  6. 类型推断:自动推断和提取类型信息

最佳实践

  1. 渐进式采用:从简单类型开始,逐步使用高级特性
  2. 保持可读性:复杂的类型应该有清晰的注释
  3. 避免过度设计:不要为了使用高级特性而使用
  4. 类型复用:将常用类型提取为可复用的类型别名

学习路径

  1. 掌握基础类型和接口
  2. 学习泛型和泛型约束
  3. 理解条件类型和映射类型
  4. 探索模板字面量类型
  5. 实践类型推断和类型守卫
  6. 组合多种特性构建复杂类型

掌握TypeScript高级类型系统,将帮助你编写出更加健壮、可维护的代码。类型安全不仅能在编译时捕获错误,还能提供更好的IDE支持和代码提示。开始在你的项目中应用这些高级特性吧!


本文首发于掘金,欢迎关注我的专栏获取更多前端技术干货!