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

为对象属性生成双返回变体的TS类型定义与实现问题

TypeScript 对象转换函数的类型错误解决

需求背景

输入一个对象:

const inObj = {name: 'hi'}

通过函数X处理后,返回包含Raw和Result两个变体的输出:

const out = {nameRaw: ref('hi'), nameResult: ref(ok('hi'))} // ok = neverthrow库的函数

类型定义尝试

  • 符合预期但存在语法错误的类型定义:

    type funcOuta<Type extends funcInObj> = {
      [Property in keyof Type as `${Property & string}Raw`]: Ref<Type[Property]>
      [Property in keyof Type as `${Property & string}Result`]: Ref<Result<Type[Property], Error>>;
    };
    

    错误原因:同一类型字面量中无法同时存在两个独立的映射类型,需用交叉类型合并。

  • 无语法错误但类型不精准的定义:Raw与Result类型重叠,使用时需手动类型收窄,不符合需求:

    type funcOutb<Type extends DtoUscResp> = {
      [Property in keyof Type as `${Property & string}${'Raw' | 'Result'}`]:
        | Ref<Type[Property]>
        | Ref<Result<Type[Property], Error>>;
    };
    
  • 已解决的精准类型定义方案(感谢@tobias):通过交叉类型实现Raw和Result各自对应正确类型

    type funcOut<Type> = 
      { [Property in keyof Type as `${Property & string}Raw`]: Type[Property] } &
      { [Property in keyof Type as `${Property & string}Result`]: Array<Type[Property]> };
    

    测试类型test = funcOut<{name:' test'}>可得到预期的精准类型。

未解决问题:函数实现的TS索引错误

实现完整可运行示例时,出现以下TS错误:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'funcOut'. No index signature with a parameter of type 'string' was found on type 'funcOut'

完整代码:

type funcArg = {[index: string] : any}
type input = funcArg &  {name: string} // {name:hi}
type funcOut<Type> = 
  {  [Property in keyof Type as `${Property & string}Raw`]: Type[Property] } &
  {  [Property in keyof Type as `${Property & string}Result`]: Array<Type[Property]> } 
type test = funcOut<{name:' test'}> // 类型符合预期,感谢@Tobias 

function X<Type extends funcArg>(inp: funcArg):funcOut<Type> {
  let key:keyof Type & string
const res:funcOut<Type> = {} as funcOut<Type>
  for(key of Object.keys(inp))
    {
      res[key+'Raw'] = inp[key] // TS错误
      res[key+'Result'] = new Array(inp[key]) // TS错误
    }
   return res
  
}

const input: input= {name:'test'}
const outcome = X<input>(input)
console.log(outcome) // 运行结果正确:{nameRaw: "test", nameResult: ["test"]}

错误原因与解决方案

错误原因

  1. Object.keys(inp)返回string[]类型,即便将key标注为keyof Type & string,TypeScript仍无法推断key+'Raw'或key+'Result'是funcOut<Type>的有效索引键。
  2. 泛型Type的约束funcArg是字符串索引签名,但funcOut<Type>的键由Type的键拼接而来,TS无法在循环中建立字符串拼接后的键与目标类型的关联。

解决方案

通过类型断言或辅助函数告知TS拼接后的键是目标类型的有效索引,以下是优化后的代码:

type funcArg = { [index: string]: any }
type input = funcArg & { name: string }
type funcOut<Type> = 
  { [Property in keyof Type as `${Property & string}Raw`]: Type[Property] } &
  { [Property in keyof Type as `${Property & string}Result`]: Array<Type[Property]> }

function X<Type extends funcArg>(inp: Type): funcOut<Type> {
  const res = {} as funcOut<Type>
  for (const [key, value] of Object.entries(inp)) {
    // 将拼接后的键断言为funcOut<Type>的有效索引
    res[`${key}Raw` as keyof funcOut<Type>] = value
    res[`${key}Result` as keyof funcOut<Type>] = [value]
  }
  return res
}

const input: input = { name: 'test' }
const outcome = X(input)
console.log(outcome) // {nameRaw: "test", nameResult: ["test"]}

关键优化点

  1. 将inp的类型改为Type而非funcArg,让TS更精准地推断泛型类型。
  2. 使用Object.entries替代Object.keys,直接获取键值对,减少类型歧义。
  3. 通过类型断言明确拼接后的键属于funcOut<Type>的索引集合,消除类型错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 23:00:57