如何使用可选变量处理 Typescript 函数重载增长

Posted

技术标签:

【中文标题】如何使用可选变量处理 Typescript 函数重载增长【英文标题】:What to do with Typescript Function Overload Growth with Optional Variables 【发布时间】:2021-10-08 10:51:00 【问题描述】:

我很难找到一种解决方案来处理打字稿中函数重载的指数级增长,问题是:

假设我有一个 findOne 函数,它获取一个 id,在数据库中查找,然后找到一个 Person:

findOne(id):Promise<Person| undefined> 
    return query().findById(id);

现在我可以在未找到 Person(id 不存在)的情况下添加一个变量来抛出,并确保在传递变量时 findOne 不会返回 undefined;

findOne(id):Promise<Person|undefined>;
findOne(id,orFail:true):Promise<Person>;
findOne(id,orFail?:true):Promise<Person|undefined> 
    if (orFail)
        return query().findById(id).throwIfNotFound();
    
    return query().findById(id);

现在,如果我有另一个变量来决定是否应该加入人员字段,会发生什么?是的,它将返回类型乘以 2(为每个前一个添加另一种可能是否具有连接字段)

再一次,如果我有更多变量,每个变量都会乘以 2,所以我想我会有 2^optionalVarialesCount findOne 函数的重载。

有没有更好的方法来做到这一点?我不想为 4 个可选变量重载 16 次,这确实使代码混乱。

【问题讨论】:

如果你有多个选项可以改变重载函数的返回类型,我认为你试图用一个函数做太多事情。我的意思是,是的,有 30 多个重载的 API(通常是因为它们是 javascript API,然后 TypeScript 类型是后来添加的,并且必须适应原始 JavaScript 的狂野西部 :-D )。如果你真的想要有多个对返回类型有影响的选项,我认为没有任何办法可以解决重载的爆炸式增长,因为重载很难编写、阅读、使用和维护。由于选项... ...无论如何都必须进行硬编码以使返回类型不同,我建议尝试将它们组合在一起并让 X 函数与每个 Y 重载,诸如此类。祝你好运,编码愉快! 我自己也得出了同样的结论,但希望错过泛型或其他东西的一些技巧,所以在这里问。顺便谢谢 是的,这是一个很好的问题。 TypeScript 非常丰富,我经常看到一些我认为它无法解决的问题的解决方案。 :-) 【参考方案1】:

首先让我们将可选的 props 定义为一个元组:

type Optional = [onFail: true, onPass: false]

您可以在Optional tuple 中定义任意数量的道具。

现在我们可以重载我们的函数了:

type Person = 
  tag: 'Person'


type Optional = [onFail: true, onPass: false]

function findOne<
  /**
   * Infer rest arguments
   */
  Rest extends Partial<Optional>,
  /**
   * 1) if length of Rest arguments extends length of Optional arguments
   * it means that all Optional arguments was provided, which in tirn means that
   * we should return Promise<Person>
   * 
   * 2) otherwise, if Rest length is less then Optional length, it means
   * that no all Optional arguments was passes, which in turn means
   * that we should return Promise<Person | undefined>
   */
  Return extends 
    all_passed: Promise<Person>,
    not_all_passed: Promise<Person | undefined>,
  [
  Rest['length'] extends Optional['length'] ? 'all_passed' : 'not_all_passed'
  ]
>(id: string, ...args: Rest): Return

function findOne(id: string, ...args: Partial<Optional>): Promise<Person | undefined> 
  return null as any


// Promise<Person | undefined> because not optional arguments are passed
const _ = findOne('id', true) 

// Promise<Person> because all optional arguments are passed
const __ = findOne('id', true, false) 

Playground

【讨论】:

以上是关于如何使用可选变量处理 Typescript 函数重载增长的主要内容,如果未能解决你的问题,请参考以下文章

TypeScript系列教程11函数的使用

你可以在 Typescript 函数中有可选的解构参数吗?

带有可选参数的 TypeScript lambda 函数

接口中的 TypeScript 可选函数

如何在 TypeScript 中使用可选链接?

如何结合解构赋值和可选链接?