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

TypeScript中返回不同类型值的最佳实践:可选参数方案是否合理?

函数可选参数设计:是否为不良实践?

我编写了一个返回游戏资源价格的函数,这些资源可通过名称或ID标识,因此为函数添加了第三个可选参数,允许调用者指定返回值的类型。但随着开发推进,函数可返回两种类型,调用时经常需要手动指定返回类型,代码的可读性和编写难度都有所提升。请问:为函数添加可选option参数来指定返回值类型是否属于不良实践?如果这属于不良实践,该如何处理这种可选性?是拆分为多个独立函数(如balanceOfBatchIdUint256、balanceOfBatchNameUint256等),还是从一开始就设定统一标准并移除可选性?

async balanceOfBatch(
  accountsAddresses: string[], 
  resources: ResourceName[], 
  options?: { resultAsUint256?: boolean, resourcesAsId: boolean }
) {
     const resourcesIds = resources.map(resourceName => bnToUint256(ResourcesToken.constants.RESOURCES_NAMES_TO_IDS[resourceName]));
     const { balances } = await this.contract.call("balanceOfBatch", [accountsAddresses, resourcesIds]);

     let balancesBatch: { [accountAddr: string]: { [resource in ResourceNameOrId]?: BigNumber | Uint256 } } = {};
     for(let i = 0; i < accountsAddresses.length; i++) {
         const key = options?.resourcesAsId ? ResourcesToken.constants.RESOURCES_NAMES_TO_IDS[resources[i]] : resources[i];
         balancesBatch[accountsAddresses[i]] = { 
             ...balancesBatch[accountsAddresses[i]],    
             [key]: options?.resultAsUint256 ? balances[i] : uint256ToBigNumber(balances[i])
         };
     }

     return balancesBatch;
}

这种带option参数控制返回类型的做法,不能直接定义为“不良实践”,但场景复杂后确实会引发可读性、维护性问题——调用者必须记住参数组合的含义,函数内部分支逻辑随选项增多而臃肿,类型推导也容易失效(比如你现在需要手动指定返回类型)。

针对这种可选性,有几种优化方向,可根据团队实际情况选择:

  • 优先统一返回标准:如果项目能达成共识,统一资源标识方式(全用ID或全用名称)和数值类型(全用BigNumber或Uint256),这是最省心的方案。函数逻辑会极度简化,调用者无需纠结参数组合,类型明确无歧义。但前提是现有代码能平稳过渡,且团队能严格执行标准。

  • 拆分独立函数:若统一标准不可行,拆分函数是更清晰的选择。比如拆成四个职责单一的函数:

    • balanceOfBatchByName:接收资源名称,返回BigNumber格式余额
    • balanceOfBatchById:接收资源ID,返回BigNumber格式余额
    • balanceOfBatchByNameAsUint256:接收资源名称,返回Uint256格式余额
    • balanceOfBatchByIdAsUint256:接收资源ID,返回Uint256格式余额
      每个函数的输入输出一目了然,调用时无需额外参数,类型推导完美工作。虽然函数数量增加,但换来的是可读性和维护性的大幅提升,后续修改某一场景逻辑也不会影响其他函数。
  • 用TypeScript重载优化现有接口:如果不想拆分函数,可通过函数重载优化类型提示。定义多个重载签名,对应不同的参数组合与返回类型,内部复用原逻辑,但对外暴露的接口更友好:

    // 重载签名1:按名称返回BigNumber
    async balanceOfBatch(accountsAddresses: string[], resources: ResourceName[], options: { resourcesAsId: false, resultAsUint256: false }): Promise<{ [accountAddr: string]: { [resource in ResourceName]?: BigNumber } }>;
    // 重载签名2:按ID返回BigNumber
    async balanceOfBatch(accountsAddresses: string[], resources: ResourceName[], options: { resourcesAsId: true, resultAsUint256: false }): Promise<{ [accountAddr: string]: { [resource in ResourceId]?: BigNumber } }>;
    // 重载签名3:按名称返回Uint256
    async balanceOfBatch(accountsAddresses: string[], resources: ResourceName[], options: { resourcesAsId: false, resultAsUint256: true }): Promise<{ [accountAddr: string]: { [resource in ResourceName]?: Uint256 } }>;
    // 重载签名4:按ID返回Uint256
    async balanceOfBatch(accountsAddresses: string[], resources: ResourceName[], options: { resourcesAsId: true, resultAsUint256: true }): Promise<{ [accountAddr: string]: { [resource in ResourceId]?: Uint256 } }>;
    // 实际实现函数
    async balanceOfBatch(
      accountsAddresses: string[], 
      resources: ResourceName[], 
      options: { resultAsUint256?: boolean, resourcesAsId: boolean }
    ) {
      // 原逻辑不变
    }
    

    这样调用时TypeScript会自动根据传入的options推导返回类型,无需手动指定,可读性也会改善很多。

总结来说:能统一标准就优先统一;无法统一时,拆分独立函数是最直观的方案;重载则是折中选项,兼顾代码复用和类型友好。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 18:10:29