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 自动推导函数返回类型
  • 泛型约束确保类型安全
  • 条件类型提供灵活的类型逻辑

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

相关推荐
我叫张得帅3 小时前
从零开始的前端异世界生活--003--“探究Domain,DNS,Hosting”
前端
一大树3 小时前
H5在不同操作系统与浏览器中的兼容性挑战及全面解决方案
前端·ios
中微子3 小时前
TypeScript never 类型详解
前端
Strawberry_rabbit3 小时前
路由配置中的svg图标如何匹配
前端·css
练习前端两年半3 小时前
🔍 你真的会二分查找吗?
前端·javascript·算法
用户52980797824983 小时前
Vue 为何自动加载 index.vue?
前端
北风GI3 小时前
element-plus 自定义主题 最佳实践
前端
CodeCraft Studio3 小时前
国产化PDF处理控件Spire.PDF教程:C#中轻松修改 PDF 文档内容
前端·pdf·c#·.net·spire.pdf·编辑pdf·修改pdf
晴殇i3 小时前
告别 localStorage!探索前端存储新王者 IndexedDB
前端·javascript·面试