TypeScript 中所有重载参数类型的类型

szy*_*ski 6 typescript

考虑以下示例:

class A {
  private constructor(public n: number) {}
  public getDouble() {
    return this.n * 2;
  }
  static from(s: string): A;
  static from(n: number): A;
  static from(n1: number, n2: number): A;
  static from(...args: unknown[]): A {
    if (args.length === 1) {
      if (typeof args[0] === 'string') {
        if (args[0].length !== 1) {
          throw new Error('String must have a length of 1')
        }
        return new A(Number(args[0]));
      } else if (typeof args[0] === 'number') {
        if (args[0] > 9) {
          throw new Error('Number must be lower than 10')
        }
        return new A(args[0]);
      }
    } else if (args.length === 2) {
      if (typeof args[0] === 'number' && typeof args[1] === 'number') {
        const sum = args[0] + args[1];
        if (sum > 9) {
          throw new Error('Sum of numbers must be lower than 10')
        }
        return new A(sum);
      }
    }
    throw new Error('No overload matched')
  }
}
Run Code Online (Sandbox Code Playgroud)

的实例A只能使用静态方法 构造from,该方法被重载以获取字符串、数字或两个数字。有效参数仅包括长度为 1 的字符串、小于 10 的数字以及总和小于 10 的数字对 - 其他参数会导致错误。

我想实现另一个静态方法,它将检查是否可以从给定的参数列表构造validate的实例,如下所示:A

static validate(...args: Parameters<typeof A.from>): boolean {
  try {
    A.from(...args);
    return true;
  } catch (error) {
    return false;
  }
}
Run Code Online (Sandbox Code Playgroud)

问题在于Parameters<typeof A.from>解析为列表中最后一个重载的参数(在本例中为 )n1: number, n2: number,这会导致 TypeScript 不允许使用任何其他重载签名(即 )进行调用A.validate('foo')

我怎样才能让 TypeScript 理解validate可以使用与 相同的重载签名来调用from

我找到了两种可能的解决方案,但都有很大的缺点:

  1. 我可以手动构造另一个与所有先前重载相匹配的重载签名,并将其放在列表的末尾。然而,这将相当难以维护,并且会扰乱 IntelliSense 提示。
  2. 与 1. 类似,但签名允许...args: unknown[]. 然而,这会让 TypeScript 认为from实际上可以使用任何参数来调用,而实际上它不能。
  3. 我可以复制并粘贴所有from重载签名并声明它们validate。同样,难以维护 - 实际用例涉及更多的重载以及多个模仿from重载的静态方法,这将导致 50 多行重载声明。

还有其他方法可以解决这个问题吗?有没有办法写一个OverloadParameters类型?

jca*_*alz 5

如果您有一个重载函数/方法,其中每个调用签名都具有相同的返回类型from()(返回类型始终为的情况A),则可以将该函数重构为具有单个调用签名的版本,其中包含剩余参数,其类型是剩余元组并集

\n
static from(...args: [s: string] | [n: number] | [n1: number, n2: number]): A {\n  // same impl as before\n}  \n
Run Code Online (Sandbox Code Playgroud)\n

这可以像以前一样调用:

\n
A.from("okay"); // works\nA.from(1); // okay\nA.from(2, 4); // okay\nA.from("oops", 2); // error!\n
Run Code Online (Sandbox Code Playgroud)\n

事实上,从 IntelliSense 的角度来看,它甚至看起来像是一个重载函数:

\n
/* A.from(\xe2\x8e\x80) */\n// 1/3 from(s: string): A\n// 2/3 from(n: number): A\n// 3/3 from(n1: number, n2: number): A\n
Run Code Online (Sandbox Code Playgroud)\n

但至关重要的是,Parameters<typeof A["from"]>现在是元组的完整并集,并且不会丢失任何信息:

\n
static validate(...args: Parameters<typeof A.from>): boolean {\n  // same impl as before\n}\n
Run Code Online (Sandbox Code Playgroud)\n

您可以验证是否A.validate()可以通过与以下完全相同的方式调用A.from()

\n
A.validate("okay"); // works\nA.validate(1); // okay\nA.validate(2, 4); // okay\nA.validate("oops", 2); // error!\n
Run Code Online (Sandbox Code Playgroud)\n

Playground 代码链接

\n