You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript泛型工厂函数createFetcher类型推断优化需求

泛型工厂函数类型推断优化方案

问题背景

我有一个泛型工厂函数createFetcher,它接收validationStrategy并返回fetcher函数。fetcher接收schema后,返回数据的类型由validationStrategy和schema的类型共同决定。当前实现必须手动传入Key泛型参数才能正确推断返回类型(如number[]),但我希望**仅指定GenSchema(如MySchema),无需传入Key**时,fetcher仍能正确推断返回类型(而非any)。

现有实现(需手动传入Key)

const createFetcher = <
   Key extends keyof GenSchema,
   GenSchema extends Record<string, any> = Record<string, any>,
>(
   validationStrategy: (schema: GenSchema, data: any) => ReturnType<GenSchema[Key]>,
) => {
   const fetcher = async <Schema extends GenSchema>(
      schema: Schema,
   ): Promise<ReturnType<Schema[Key]>> => {
      const data = await (await fetch('/')).json()
      return await validationStrategy(schema, data)
   }

   return fetcher
}

type MySchema<T = any> = { parse: (data: T) => T }

async function main() {
   const schema: MySchema<number[]> = { parse: () => [1, 2, 3] }
   
   const fetcher = createFetcher<'parse', MySchema>((schema, data) => schema.parse(data))
   const data = await fetcher(schema)
   //    ^?   number[]  ->  类型推断正确
}

期望实现(无需传入Key)

当前直接指定GenSchema时,返回类型被推断为any,不符合预期:

type MySchema<T = any> = { parse: (data: T) => T }

async function main(){
   const schema: MySchema<number[]> = { parse: () => [1, 2, 3] }

   const fetcher = createFetcher<MySchema>((schema, data) => 
   schema.parse(data))
   const data = await fetcher(schema)
   //    ^? any   ->  不符合预期,应推断为number[]
}

注:validationStrategy的类型需兼容type ValidationStrategy<Schema=any> = (schema: Schema, data: any) => any,schema可为任意对象。


解决方案

核心思路是让createFetcher的泛型直接绑定到验证策略的完整类型,利用TypeScript的工具类型和条件推断自动推导返回值类型,无需手动指定Key。

优化后的实现

type ValidationStrategy<Schema = any> = (schema: Schema, data: any) => any;

const createFetcher = <T extends ValidationStrategy>(validationStrategy: T) => {
  // 从验证策略中提取Schema类型和动态返回类型
  type BaseSchema = Parameters<T>[0];
  
  const fetcher = async <S extends BaseSchema>(
    schema: S
  ): Promise<T extends (s: S, d: any) => infer R ? R : never> => {
    const data = await (await fetch('/')).json();
    return validationStrategy(schema, data) as T extends (s: S, d: any) => infer R ? R : never;
  };

  return fetcher;
};

type MySchema<T = any> = { parse: (data: T) => T };

async function main() {
  const schema: MySchema<number[]> = { parse: () => [1, 2, 3] };

  // 无需手动传入泛型参数,TypeScript自动完成推断
  const fetcher = createFetcher((schema, data) => schema.parse(data));
  const data = await fetcher(schema);
  //    ^? number[]  -> 类型推断正确
}

更简洁的版本

如果不需要单独定义ValidationStrategy类型,可直接通过泛型参数捕获输入输出关系:

const createFetcher = <Schema, Output>(
  validationStrategy: (schema: Schema, data: any) => Output
) => {
  return async <S extends Schema>(
    schema: S
  ): Promise<Output extends (s: S) => infer R ? R : Output> => {
    const data = await (await fetch('/')).json();
    return validationStrategy(schema, data) as any;
  };
};

type MySchema<T = any> = { parse: (data: T) => T };

async function main() {
  const schema: MySchema<number[]> = { parse: () => [1, 2, 3] };

  const fetcher = createFetcher((schema: MySchema<any>, data) => schema.parse(data));
  const data = await fetcher(schema);
  //    ^? number[]  -> 类型推断正确
}

关键优化点

  • 移除了Key泛型参数,TypeScript通过验证策略函数的参数和返回值自动推断类型
  • 使用Parameters<T>工具类型提取验证策略的Schema输入类型
  • 利用条件类型infer动态推导不同Schema实例对应的返回类型
  • 支持任意结构的schema对象,符合ValidationStrategy的类型要求

内容的提问来源于stack exchange,提问作者L.Blondy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 11:57:56