打字稿:将泛型函数类型参数的 Parameters<T> 元组作为可变参数传递

时间:2021-02-03 22:34:54

标签: typescript generics types typescript4.0

在 Typescript 4.1.x 中,您可以将 spread tuple types 作为可变参数传入函数。

type MyTuple = [string, number];
const myTuple: MyTuple = ["blob", 42];

function tupleFunc(s: string, n: number): void {
    console.log(`${s} ${n}`);
}

tupleFunc(...myTuple); // ✅ A-Ok

但是,当元组派生自泛型类型参数并使用 Parameters<T> utility type 时,我遇到了错误。

function foo(a: number, b: boolean, c: string) {
  return 10;
}

foo(1, true, "baz") // 10 ✅

function bar(...params: Parameters<typeof foo>) {
    return foo(...params)
}

bar(1, true, "baz") // 10 ✅

function bar2<F extends typeof foo>(...params: Parameters<F>) {
  //  next line would work
  //  return foo(params[0], params[1], params[2])

  return foo(...params); // Fails ?
  // Expected 3 arguments, but got 0 or more.ts(2556)
  // index.ts(28, 14): An argument for 'a' was not provided.
}

有没有办法让这个概念通过类型检查器,或者它在打字稿中不受支持?虽然它出错了,但它似乎在 Typescript 沙箱中工作。 See an example here.

似乎我可以让它与 .apply() 一起工作,但我很想知道是否还有其他方法。

function bar3<T extends typeof foo>(...params: Parameters<T>) {
  return foo.apply(null, params);
}

1 个答案:

答案 0 :(得分:1)

这很有趣,我不知道是否有关于它的规范 GitHub 问题。还没找到;如果我找到了,我会回来编辑的。我对错误原因的最佳猜测是函数实现中的 Parameters<F> utility type 是“未解析的通用条件类型”;编译器不知道 F 是什么,并且在它知道之前不想承诺评估 Parameters<F>。它不会在函数内部,除非您尝试将 params 分配给另一个变量或使用类型断言。


编译器显然不知道 F,不管它是什么,都会有与 foo 一样多的参数,所以它给出了一个错误。事实证明,bar2() 实现是不安全的。

TypeScript 的可分配性规则之一是参数较少的函数可以分配给参数较多的函数。请参阅 the FAQ entry on the subject 了解为什么这是可取的(简短回答:假设函数只会忽略额外的参数通常是安全的,这就是大多数人编写不需要所有传入参数的回调的方式):< /p>

const baz = () => "hello";
const assignableToFoo: typeof foo = baz; // no error

允许此赋值的事实意味着 F extends typeof foo 可以指定为您不想要的东西。想象一下 foo() 做了一些真正关心其参数类型的事情:

function foo(a: number, b: boolean, c: string): string {
  return `success ${a.toFixed(2)} ${b} ${c.toUpperCase()}`;
}

然后你可以像这样调用 bar2(),根据它的定义:

console.log(bar2<typeof baz>()); // compiles fine, but:
// RUNTIME ERROR ? TypeError: a.toFixed() no good if a is undefined!

既然 Ftypeof baz,那么 Parameters<F>[]bar2() 可以不带参数调用,params 可能是空的。 bar2 中的错误正确地警告您 foo(...params) 具有潜在危险。


现在,因为您说这是一个简化的示例,所以我不能 100% 确定如何最好地编写捕获所需用例的 bar2 签名版本。通常泛型类型参数应该对应一些实际值;但是在调用 F 时不涉及 bar2() 类型的值,只是一个类型与其参数列表相同的值。根据编写的示例代码,我想说您应该只使用非泛型 bar()


最后,如果您决定不关心 Ffoo 更窄以缩短其参数列表的可能性,那么您可以假装 { {1}} 是 params 类型(实际上我认为编译器会让你不安全地将它“扩展”为该类型的变量,即使它可能不应该):

Parameters<typeof foo>

但要小心!


Playground link to code

相关问题