TypeScript 泛型与 ReturnType 详解

TypeScript 泛型与 ReturnType 详解

泛型基础概念

泛型(Generics)是 TypeScript 中创建可重用组件的核心特性,它允许我们在定义函数、接口或类时使用类型参数,而不是具体的类型。

基础泛型函数

typescript 复制代码
// 最简单的泛型函数
function identity<T>(arg: T): T {
  return arg;
}

// 带有额外属性的泛型函数
function fetchData<T>(data: T) {
  return {
    data,
    timestamp: Date.now()
  }
}

ReturnType 与泛型函数

ReturnType 是 TypeScript 内置的工具类型,用于获取函数的返回类型。当与泛型函数结合使用时,它可以推导出具体的返回类型。

基本用法

typescript 复制代码
function fetchData<T>(data: T) {
  return {
    data,
    timestamp: Date.now()
  }
}

// 使用 ReturnType 获取具体泛型实例的返回类型
type StringResult = ReturnType<typeof fetchData<string>>;
// 等价于: { data: string; timestamp: number }

type NumberResult = ReturnType<typeof fetchData<number>>;
// 等价于: { data: number; timestamp: number }

泛型约束

泛型约束允许我们限制泛型参数的类型范围。

接口约束

typescript 复制代码
interface HasId {
  id: number;
}

function processWithId<T extends HasId>(item: T) {
  return {
    ...item,
    processedAt: new Date()
  }
}

type ProcessedResult = ReturnType<typeof processWithId<{ id: number; name: string }>>;
// { id: number; name: string; processedAt: Date }

联合类型约束

typescript 复制代码
function handleUnion<T extends string | number>(value: T): T {
  return value;
}

type StringResult = ReturnType<typeof handleUnion<string>>; // string
type NumberResult = ReturnType<typeof handleUnion<number>>; // number

多个泛型参数

typescript 复制代码
function createPair<K, V>(key: K, value: V): [K, V] {
  return [key, value];
}

type StringNumberPair = ReturnType<typeof createPair<string, number>>; // [string, number]
type BooleanStringPair = ReturnType<typeof createPair<boolean, string>>; // [boolean, string]

泛型默认值

typescript 复制代码
function createConfig<T = string>(value: T) {
  return {
    value,
    createdAt: Date.now()
  }
}

// 使用默认类型
type DefaultConfig = ReturnType<typeof createConfig>; // { value: string; createdAt: number }

// 指定具体类型
type NumberConfig = ReturnType<typeof createConfig<number>>; // { value: number; createdAt: number }

泛型与条件类型

typescript 复制代码
// 条件返回类型
function transformValue<T>(value: T): T extends string ? string : T extends number ? number : T {
  if (typeof value === 'string') {
    return value.toUpperCase() as any;
  }
  return value as any;
}

type StringTransform = ReturnType<typeof transformValue<string>>; // string
type NumberTransform = ReturnType<typeof transformValue<number>>; // number
type BooleanTransform = ReturnType<typeof transformValue<boolean>>; // boolean

泛型函数重载

typescript 复制代码
// 函数重载声明
function parseInput<T extends string | number>(input: T): T extends string ? string : number;

// 函数实现
function parseInput(input: any): any {
  if (typeof input === 'string') {
    return input.trim();
  }
  return Number(input);
}

type StringParse = ReturnType<typeof parseInput<string>>; // string
type NumberParse = ReturnType<typeof parseInput<number>>; // number

异步函数与泛型

typescript 复制代码
async function fetchApi<T>(url: string): Promise<T> {
  const response = await fetch(url);
  return response.json();
}

type ApiResponse = ReturnType<typeof fetchApi<{ data: string }>>;
// Promise<{ data: string }>

// 提取 Promise 的泛型类型
type UnpackPromise<T> = T extends Promise<infer U> ? U : T;
type ApiData = UnpackPromise<ApiResponse>; // { data: string }

泛型类方法

typescript 复制代码
class DataProcessor<T> {
  process(data: T) {
    return {
      original: data,
      processed: true,
      timestamp: Date.now()
    };
  }
}

const stringProcessor = new DataProcessor<string>();
type StringProcessed = ReturnType<typeof stringProcessor.process>;
// { original: string; processed: boolean; timestamp: number }

const numberProcessor = new DataProcessor<number>();
type NumberProcessed = ReturnType<typeof numberProcessor.process>;
// { original: number; processed: boolean; timestamp: number }

泛型与映射类型

typescript 复制代码
function createRecord<K extends string, V>(keys: K[], value: V): Record<K, V> {
  const result = {} as Record<K, V>;
  keys.forEach(key => {
    result[key] = value;
  });
  return result;
}

type StringRecord = ReturnType<typeof createRecord<['name', 'age'], string>>;
// Record<"name" | "age", string>

type NumberRecord = ReturnType<typeof createRecord<['count', 'total'], number>>;
// Record<"count" | "total", number>

实际应用场景

API 响应处理

typescript 复制代码
interface User {
  id: number;
  name: string;
  email: string;
}

function createUserResponse<T extends User>(user: T) {
  return {
    success: true,
    data: user,
    timestamp: Date.now()
  };
}

type UserResponse = ReturnType<typeof createUserResponse<User>>;
// { success: boolean; data: User; timestamp: number }

错误处理模式

typescript 复制代码
function tryCatch<T>(fn: () => T): { success: true; data: T } | { success: false; error: Error } {
  try {
    return { success: true, data: fn() };
  } catch (error) {
    return { success: false, error: error as Error };
  }
}

type StringResult = ReturnType<typeof tryCatch<() => string>>;
// { success: true; data: string } | { success: false; error: Error }

type NumberResult = ReturnType<typeof tryCatch<() => number>>;
// { success: true; data: number } | { success: false; error: Error }

高阶函数

typescript 复制代码
function withLogging<T extends (...args: any[]) => any>(fn: T): T {
  return ((...args: Parameters<T>) => {
    console.log(`Calling function with args:`, args);
    const result = fn(...args);
    console.log(`Function returned:`, result);
    return result;
  }) as T;
}

const loggedFetchData = withLogging(fetchData);
type LoggedStringResult = ReturnType<typeof loggedFetchData<string>>;
// { data: string; timestamp: number }

高级技巧

类型推断与泛型

typescript 复制代码
function inferType<T>(value: T) {
  return {
    value,
    type: typeof value
  };
}

type StringInferred = ReturnType<typeof inferType<string>>;
// { value: string; type: "string" }

type NumberInferred = ReturnType<typeof inferType<number>>;
// { value: number; type: "number" }

递归泛型类型

typescript 复制代码
// 深度只读类型
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object
    ? T[P] extends Function
      ? T[P]
      : DeepReadonly<T[P]>
    : T[P];
};

function createReadonlyConfig<T>(config: T): DeepReadonly<T> {
  return JSON.parse(JSON.stringify(config)) as DeepReadonly<T>;
}

type ReadonlyConfig = ReturnType<typeof createReadonlyConfig<{ api: { url: string } }>>;
// DeepReadonly<{ api: { url: string } }>

常见问题与解决方案

问题1:ReturnType 与泛型函数签名

typescript 复制代码
// 错误:不能直接对泛型函数使用 ReturnType
// type Wrong = ReturnType<typeof fetchData>; // 错误!

// 正确:需要指定具体的泛型参数
type Correct = ReturnType<typeof fetchData<string>>;

问题2:泛型约束与 ReturnType

typescript 复制代码
// 当泛型有约束时,ReturnType 会考虑约束
function processArray<T extends any[]>(arr: T): T {
  return arr;
}

type ArrayResult = ReturnType<typeof processArray<number[]>>; // number[]

问题3:条件类型与 ReturnType

typescript 复制代码
// 条件类型在 ReturnType 中的行为
function conditional<T>(value: T): T extends string ? "string" : "other" {
  return (typeof value === 'string' ? "string" : "other") as any;
}

type StringCondition = ReturnType<typeof conditional<string>>; // "string"
type NumberCondition = ReturnType<typeof conditional<number>>; // "other"

最佳实践

1. 合理使用泛型约束

  • 使用约束确保类型安全
  • 避免过度约束限制灵活性
  • 在文档中说明约束的含义

2. 利用 ReturnType 进行类型推导

  • 使用 ReturnType 避免重复定义类型
  • 在复杂函数中利用 ReturnType 推导返回类型
  • 结合泛型创建灵活的类型系统

3. 类型安全与性能

  • 泛型在编译时被擦除,不影响运行时性能
  • 使用泛型约束在编译时捕获类型错误
  • 避免不必要的复杂泛型嵌套

总结

泛型与 ReturnType 的结合为 TypeScript 提供了强大的类型推导能力:

  • 泛型提供了代码重用和类型安全
  • ReturnType 自动推导函数返回类型
  • 泛型约束确保类型安全
  • 条件类型提供灵活的类型逻辑

通过合理使用这些特性,可以创建出既类型安全又灵活可重用的代码库。

相关推荐
__zRainy__2 分钟前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.40 分钟前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~2 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华2 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子5 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅5 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni5 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学6 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端
赵庆明老师7 小时前
Vben精讲:21-详解web-antd:tsconfig.json
前端·json·vim