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

如何让createParser的配置对象自动匹配parse的参数与返回值类型

TypeScript 类型约束:确保Parser的defaultValue与parse入参、defaultValueOut与parse返回值匹配

我之前在Stack Overflow提过类似问题(感谢用户jcalz),但现在条件变更后问题更复杂。以下是我最简洁的尝试代码:

type ParserFunction<In, Out> = (value: In) => Out;

interface Parser<In, Out> {
  parse: ParserFunction<In, Out>;
  defaultValue?: In;
  defaultValueOut?: Out;
}

function createParser<
  TParser extends Parser<any, any>,
  TFields extends Record<string, TParser>
>(options: {
  [K in keyof TFields]: TFields[K] extends Parser<infer I, infer O> ? Parser<I, O> : never
}) {
  return void 0;
}

createParser({
  pass: {
    defaultValue: '100',
    defaultValueOut: 100,
    parse(value: string) {
      return Number(value)
    }
  },
  fail: {
    defaultValue: '100', // 此处应为number类型
    defaultValueOut: 100, // 此处应为string类型
    parse(value: number) {
      return String(value)
    }
  }
})

需要实现的效果是:调用createParser时,每个Parser对象的defaultValue类型必须与parse的入参类型一致,defaultValueOut类型必须与parse的返回值类型一致。


解决方案

问题出在原代码的泛型约束上:TParser extends Parser<any, any>会让所有字段共享同一种In和Out类型,导致TypeScript无法为每个字段独立推断类型。我们需要修改泛型定义,让每个字段可以拥有自己的In和Out类型。

方案一:简化泛型约束

直接让TFields是Record<string, Parser<any, any>>,利用TypeScript的类型推断自动为每个字段匹配正确的In和Out:

type ParserFunction<In, Out> = (value: In) => Out;

interface Parser<In, Out> {
  parse: ParserFunction<In, Out>;
  defaultValue?: In;
  defaultValueOut?: Out;
}

function createParser<TFields extends Record<string, Parser<any, any>>>(
  options: {
    [K in keyof TFields]: TFields[K] extends Parser<infer I, infer O> 
      ? Parser<I, O> 
      : never
  }
) {
  return void 0;
}

createParser({
  pass: {
    defaultValue: '100',
    defaultValueOut: 100,
    parse(value: string) {
      return Number(value)
    }
  },
  fail: {
    defaultValue: 100, // 类型正确,传入string会触发错误
    defaultValueOut: '100', // 类型正确,传入number会触发错误
    parse(value: number) {
      return String(value)
    }
  }
})

方案二:使用辅助类型明确约束

如果需要更清晰的类型关系,可以定义一个辅助类型ParserRecord,直接从每个字段的parse函数推断In和Out,再约束整个对象的类型:

type ParserFunction<In, Out> = (value: In) => Out;

interface Parser<In, Out> {
  parse: ParserFunction<In, Out>;
  defaultValue?: In;
  defaultValueOut?: Out;
}

type ParserRecord<T extends Record<string, { parse: (...args: any) => any }>> = {
  [K in keyof T]: 
    T[K]['parse'] extends (value: infer I) => infer O 
      ? Parser<I, O> 
      : never
};

function createParser<T extends Record<string, { parse: (...args: any) => any }>>(
  options: ParserRecord<T>
) {
  return void 0;
}

createParser({
  pass: {
    defaultValue: '100',
    defaultValueOut: 100,
    parse(value: string) {
      return Number(value)
    }
  },
  fail: {
    defaultValue: 100,
    defaultValueOut: '100',
    parse(value: number) {
      return String(value)
    }
  }
})

两种方案都能实现需求:当defaultValue或defaultValueOut的类型与parse的入参/返回值不匹配时,TypeScript会立即抛出类型错误。


内容的提问来源于stack exchange,提问作者Jordan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 15:33:09