在封装通用函数时,泛型参数经常无法自动推断,需要手动指定。常见原因包括:泛型参数未出现在参数列表中、函数重载干扰、泛型约束过于复杂等。理解这些场景有助于写出更易用的泛型 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 }
function fn<T = DefaultType>() 给泛型设置默认值,在无法推断时回退到默认类型,避免推断为 unknown。config: RequestConfig<T>),但要注意如果 T 在参数中也没有实际使用,仍然无法推断。T extends HasId 不会帮助推断,它只是约束。但约束后的泛型可以从实际参数中推断出具体类型。fn<ConcreteType>() 手动指定。// 实战:封装一个类型安全的 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