在 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);
}
答案 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!
既然 F
是 typeof baz
,那么 Parameters<F>
是 []
,bar2()
可以不带参数调用,params
可能是空的。 bar2
中的错误正确地警告您 foo(...params)
具有潜在危险。
现在,因为您说这是一个简化的示例,所以我不能 100% 确定如何最好地编写捕获所需用例的 bar2
签名版本。通常泛型类型参数应该对应一些实际值;但是在调用 F
时不涉及 bar2()
类型的值,只是一个类型与其参数列表相同的值。根据编写的示例代码,我想说您应该只使用非泛型 bar()
。
最后,如果您决定不关心 F
比 foo
更窄以缩短其参数列表的可能性,那么您可以假装 { {1}} 是 params
类型(实际上我认为编译器会让你不安全地将它“扩展”为该类型的变量,即使它可能不应该):
Parameters<typeof foo>
但要小心!