← 返回导航

泛型推断失败的常见原因

踩坑记录

场景说明

在封装通用函数时,泛型参数经常无法自动推断,需要手动指定。常见原因包括:泛型参数未出现在参数列表中、函数重载干扰、泛型约束过于复杂等。理解这些场景有助于写出更易用的泛型 API。

完整代码


      
// 泛型推断失败的常见场景与解决方案

// 场景一:泛型只在返回值中出现,无法推断
function fetchData<T>(url: string): Promise<T> {
  return fetch(url).then(res => res.json());
}
// const data = fetchData('/api/users');  // T 推断为 unknown
// 解决:手动指定泛型
const data = fetchData<User>('/api/users');

// 场景二:泛型参数未被参数直接使用
function createMap<K extends string, V>(): Map<K, V> {
  return new Map();
}
// const map = createMap();  // 推断为 Map<string, unknown>
const map = createMap<string, number>();  // 必须手动指定

// 场景三:使用默认泛型参数
function request<T = unknown>(url: string): Promise<T> {
  return fetch(url).then(res => res.json());
}
// 不指定时 T 默认为 unknown,至少比推断失败好

// 场景四:封装通用请求函数时泛型推断失败
interface RequestConfig<T> {
  url: string;
  method?: 'GET' | 'POST';
  data?: unknown;
  // T 没有出现在参数中,无法推断
}

async function request2<T>(config: RequestConfig<T>): Promise<T> {
  const res = await fetch(config.url);
  return res.json();
}
// const users = await request2({ url: '/api/users' });  // T 推断为 unknown

// 解决:让 T 从参数中可推断
async function request3<T>(config: {
  url: string;
  method?: 'GET' | 'POST';
  data?: T;  // data 类型与 T 关联,可以从 data 推断 T
}): Promise<T> {
  const res = await fetch(config.url);
  return res.json();
}

// 场景五:泛型约束过于复杂导致推断失败
type HasId = { id: number };
function findById<T extends HasId>(list: T[], id: number): T | undefined {
  return list.find(item => item.id === id);
}
const users = [{ id: 1, name: 'Alice' }];
const user = findById(users, 1);  // T 推断为 { id: number; name: string }

关键点解析

使用示例与类型推导


      
// 实战:封装一个类型安全的 API 请求函数

interface ApiResponse<T> {
  code: number;
  data: T;
  message: string;
}

// 方案一:泛型只在返回值 -- 需要手动指定
async function get<T>(url: string): Promise<ApiResponse<T>> {
  const res = await fetch(url);
  return res.json();
}
// const result = await get<User[]>('/api/users');

// 方案二:用 identity 函数辅助推断
function createApi<T>() {
  return {
    async get(url: string): Promise<ApiResponse<T>> {
      const res = await fetch(url);
      return res.json();
    }
  };
}
const userApi = createApi<User>();
// const result = await userApi.get('/api/users/1');  // 自动推断为 User
在 Playground 中尝试