typescript “任意函数”的替代选项((...args:any[])=>any)而不使用任何

8zzbczxx  于 2023-03-09  发布在  TypeScript
关注(0)|答案(1)|浏览(274)

我想知道是否有一种替代方法来指定不使用any的函数类型“any function”(从而提高类型安全性)。为了澄清,我正在寻找以下类型的替代方法:

type AnyFunctionAsUsual = (...args: any[]) => any;

我可以将返回值替换为unknown,但在下面的定义中仍然存在any

type AnyFunctionReturningUnknown = (...args: any[]) => unknown;

据我所知,类型AnyFunctionAsUsualAnyFunctionReturningUnknown可以取相同的值,而且我还认为后者是足够类型安全的(因为就我而言,参数的any没有禁用任何类型检查)。
但是eslint不喜欢它,因为它仍然违反了no-explicit-any规则,该规则包含在plugin:@typescript-eslint/recommended中。
有没有一种方法可以定义一个类型,它可以和上面的两个类型具有相同的值,并且还可以使linter满意?(禁用linter或规则并不等同于使linter满意。)

s5a0g9ez

s5a0g9ez1#

你想让SomeFunction成为top type的函数,如果你有一个f的函数,你可以把它赋给SomeFunction类型的变量,如果你有一个x的非函数,那么你 * 不能 * 把它赋给SomeFunctionunknown类型的函数排序。(你不能使用unknown,因为它接受非函数。)
你的(...args: any) => unknown(...args: any[]) => unknown类型满足这些条件,但是它们不是类型安全的,因为它们使用了不安全的any“类型”。在安全类型系统中,有一个权衡,顶级类型的值容易 * 提供 *,但很难 * 消费 *。如果我想要unknown类型的值,你可以给予我任何你想要的。但是一旦我有了它,如果不进一步检查它,我不知道该怎么做。如果我想要一个SomeFunction类型的值,你可以给予我任何你想要的 function。所以一旦我有了它,我就可以做任何对 arbitrary 函数有效的事情,比如检查它的length属性或者......嗯......我不能用它做很多事情。我不能调用它,因为我不知道它会接受什么参数。所以使用SomeFunction将是困难的,或者至少是棘手的。(...args: any) => unknown类型允许你调用它 * 无论你想要什么 *,即使这会立即导致运行时错误:

type SomeFunction = (...args: any) => unknown;
const f: SomeFunction = (x: string) => x.toUpperCase();
f(); // accepted erroneously, runtime error!
f(1, 2, 3); // accepted erroneously, runtime error!

这就是为什么不应该在这里使用any的原因。
假设您想要一个SomeFunction的 * 安全 * 版本,那么我们应该确保参数列表没有被赋予“anything-goes”类型。它应该是相反的,你应该完全不能调用它,那是因为函数类型在它们的参数类型中是逆变的(更多信息请参见Difference between Variance, Covariance, Contravariance and Bivariance in TypeScript--strictFunctionTypes编译器选项的描述)。在TypeScript中,这意味着顶级函数类型的参数列表应该为bottom type。底部类型为never类型:

type SomeFunction = (...args: never) => unknown;
const f: SomeFunction = (x: string) => x.toUpperCase();
f(); // error in TS5.0 and above
f(1, 2, 3); // error

(...args: never) => unknown充当函数顶级类型的能力主要在microsoft/TypeScript#35438中实现,并在microsoft/TypeScript#52387中完成,microsoft/TypeScript#52387将随TypeScript 5.0发布。在此之前,可以无错误地调用上面的f()(即使它不安全),但其他调用将被拒绝。
Playground代码链接

相关问题