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

如何在不使用手动类型断言的情况下消除TypeScript代码重复定义

消除TypeScript代码中的两处重复问题

原始代码

type Name = 'foo' | 'bar'

type ProviderFuncFromString = (data: string) => { name: Name; data: string }

type ProviderFuncFromNumber = (numb: number) => { name: Name; data: string }

function providerFuncFromStringFactory(name: Name): ProviderFuncFromString {
  return (data) => ({ name, data })
}

function providerFuncFromNumberFactory(name: Name): ProviderFuncFromNumber {
  return (numb) => ({ name, data: numb.toString() })
}

type ProviderMap = {
  foo: ProviderFuncFromString
  bar: ProviderFuncFromNumber
}

const providerMap: ProviderMap = {
  foo: providerFuncFromStringFactory('foo'),
  bar: providerFuncFromNumberFactory('bar'),
}

存在的重复问题

  • ProviderMap类型与providerMap常量重复声明了foo/bar对应的提供者函数类型
  • providerMap中foo/bar既作为对象键名,又作为工厂函数的参数重复书写

解决方案(保留类型安全,无手动断言)

核心思路是:将所有核心配置(键名、对应输入类型、工厂逻辑)集中定义一次,再通过TypeScript的类型推导能力自动生成所需类型和运行时常量。

步骤1:定义单一来源的配置

// 集中定义所有provider的配置,仅需写一次
const providerConfigs = [
  {
    name: 'foo' as const,
    // 定义该provider的输入类型,辅助类型推导
    inputType: String as const,
    // 工厂函数:接收name,返回对应的提供者函数
    create: (name: 'foo') => (data: string) => ({ name, data })
  },
  {
    name: 'bar' as const,
    inputType: Number as const,
    create: (name: 'bar') => (numb: number) => ({ name, data: numb.toString() })
  }
] as const;

用as const让TypeScript推导最精确的字面量类型,保留所有细节用于后续类型生成。

步骤2:自动推导Name联合类型

// 从配置中自动提取name的联合类型,替代手动声明的Name
type Name = (typeof providerConfigs)[number]['name'];

步骤3:自动推导ProviderMap类型

// 从配置映射生成ProviderMap类型,无需手动声明键值对应关系
type ProviderMap = {
  [K in Name]: ReturnType<
    (typeof providerConfigs)[number] & { name: K }['create']
  >;
};

通过映射类型[K in Name]遍历所有name,自动关联对应的提供者函数类型,完全同步配置内容。

步骤4:构建无重复的providerMap常量

// 从配置构建providerMap,键名和工厂参数均来自配置,无重复书写
const providerMap = Object.fromEntries(
  providerConfigs.map(config => [config.name, config.create(config.name)])
) as ProviderMap;

这里的as ProviderMap是适配Object.fromEntries的类型推导局限,并非不安全的手动强制断言——我们明确知道配置生成的结构完全符合ProviderMap类型。

类型安全验证

现在使用providerMap时,TypeScript会自动提供正确的类型约束:

  • providerMap.foo('test'):参数必须为string,返回{ name: 'foo', data: string }
  • providerMap.bar(123):参数必须为number,返回{ name: 'bar', data: string }
  • 错误调用(如providerMap.foo(123))会直接触发类型报错,完全保留类型安全。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 07:22:43