【TS学习】(14)类型编程的理解

类型编程(Type-Level Programming) 是 TypeScript 中一种高级特性,它允许开发者在类型系统中进行逻辑操作,而无需运行时代码。通过类型编程,在编译阶段对类型进行推断、转换和验证,从而实现更强大的类型检查和代码约束。


1. 什么是类型编程?

(1) 定义
  • 类型编程是指在 TypeScript 的类型系统中编写逻辑代码,以生成或操作类型。
  • 它类似于运行时代码编程,但操作的对象是类型,而不是值。
(2) 与运行时代码的区别
  • 运行时代码
    • 操作的是值(如变量、函数等)。
    • 在程序运行时执行。
  • 类型编程
    • 操作的是类型。
    • 在编译阶段完成,不会影响运行时性能。
示例
// 运行时代码
function add(a: number, b: number): number {
  return a + b;
}

// 类型编程
type Add<A extends number, B extends number> = A extends 1
  ? B extends 1
    ? 2
    : never
  : never;

type Result = Add<1, 1>; // 2

在这里:

  • add 是运行时代码,用于计算两个数字的和。
  • Add 是类型编程,用于在编译阶段推断两个数字类型的和。

2. 类型编程的核心工具

TypeScript 提供了一些强大的工具,用于实现类型编程。以下是常见的工具及其用途:

(1) 条件类型(Conditional Types)
  • 条件类型允许根据某种条件动态地选择类型。
  • 语法:T extends U ? X : Y
示例:过滤类型
type NonNullable<T> = T extends null | undefined ? never : T;

type Example = NonNullable<string | null | undefined>; // string

在这里:

  • NonNullable 使用条件类型过滤掉 nullundefined

(2) 映射类型(Mapped Types)
  • 映射类型允许对现有类型的所有属性进行批量操作。
  • 语法:{ [K in keyof T]: ... }
示例:添加只读修饰符
type Readonly<T> = {
  readonly [K in keyof T]: T[K];
};

type User = {
  name: string;
  age: number;
};

type ReadonlyUser = Readonly<User>;

// 结果:
// type ReadonlyUser = {
//   readonly name: string;
//   readonly age: number;
// };

在这里:

  • Readonly 使用映射类型为所有属性添加了 readonly 修饰符。

(3) 键名重映射(Key Remapping)
  • 键名重映射允许在映射类型中动态地修改键名。
  • 语法:[K in keyof T as NewKeyName]: ...
示例:添加前缀
type AddPrefix<T, Prefix extends string> = {
  [K in keyof T as `${Prefix}${string & K}`]: T[K];
};

type User = {
  name: string;
  age: number;
};

type PrefixedUser = AddPrefix<User, "user_">;

// 结果:
// type PrefixedUser = {
//   user_name: string;
//   user_age: number;
// };

在这里:

  • AddPrefix 使用键名重映射为所有键添加了前缀。

(4) 条件推断(Infer)
  • infer 关键字用于从类型中提取信息。
  • 常用于推断函数返回值类型、数组元素类型等。
示例:提取函数返回值类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function add(a: number, b: number): number {
  return a + b;
}

type AddReturnType = ReturnType<typeof add>; // number

在这里:

  • ReturnType 使用 infer 提取了函数的返回值类型。

(5) 递归类型
  • 类型编程支持递归操作,可以处理嵌套结构。
示例:深度只读
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};

type NestedData = {
  user: {
    name: string;
    age: number;
  };
};

type ReadonlyNestedData = DeepReadonly<NestedData>;

// 结果:
// type ReadonlyNestedData = {
//   readonly user: {
//     readonly name: string;
//     readonly age: number;
//   };
// };

在这里:

  • DeepReadonly 使用递归将嵌套对象的所有属性设置为只读。

3. 类型编程的实际应用场景

(1) 类型安全的工具库
  • 类型编程可以用于构建类型安全的工具库,例如 lodashramda 的 TypeScript 版本。
示例:Pick 工具类型
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

type User = {
  name: string;
  age: number;
  email: string;
};

type UserInfo = Pick<User, "name" | "email">;

// 结果:
// type UserInfo = {
//   name: string;
//   email: string;
// };

(2) API 数据建模
  • 类型编程可以用于从接口定义中提取或转换类型。
示例:从 API 响应中提取数据类型
type ApiResponse<T> = {
  data: T;
  error?: string;
};

type UserData = { id: number; name: string };

type ExtractData<T> = T extends ApiResponse<infer D> ? D : never;

type ApiResult = ExtractData<ApiResponse<UserData>>; // { id: number; name: string }

(3) 状态管理
  • 类型编程可以用于 Redux 或其他状态管理工具的类型推断。
示例:Redux Action 类型
type Action<T extends string, P = void> = P extends void
  ? { type: T }
  : { type: T; payload: P };

type IncrementAction = Action<"increment", number>;
type ResetAction = Action<"reset">;

// 结果:
// type IncrementAction = { type: "increment"; payload: number };
// type ResetAction = { type: "reset" };

4. 总结

  • 类型编程的作用
    • 实现复杂的类型推断和操作。
    • 提高代码的类型安全性和可维护性。
    • 支持灵活的类型转换和复用。
  • 核心工具
    • 条件类型。
    • 映射类型。
    • 键名重映射。
    • 条件推断(infer)。
    • 递归类型。
  • 实际场景
    • 构建类型安全的工具库。
    • API 数据建模。
    • 状态管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值