替代 “any function” ((…args: any[]) => any) 而不使用 any 的方式

huangapple go评论76阅读模式
英文:

Alternative for "any function" ((...args: any[]) => any) without using any

问题

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

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满意。)

英文:

I wonder if there is an alternative way to specify the function type "any function" that does not use any (and therefore improves type-safety).
To clarify, I am searching for an alternative for the following type:

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

I was able to replace the return value with unknown, but there is still an any in the following definition:

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

As far as I know, the types AnyFunctionAsUsual and AnyFunctionReturningUnknown may take the same values.
And I also think that the latter is sufficiently type-safe (because the any for the parameters does not disable any type checking, as far as I am concerned).

But eslint does not like it as it still violates the rule no-explicit-any, which is included in plugin:@typescript-eslint/recommended.

Is there a way to define a type that takes the same values as the two types above and also makes the linter happy? (Where disabling the linter or the rule is not the same as making the linter happy.)

答案1

得分: 1

以下是您要求的翻译:

你希望SomeFunction成为函数的顶级类型,这意味着如果你有一个函数f,你可以将它分配给类型为SomeFunction的变量,如果你有一个非函数的x,那么你不能将它分配给SomeFunction。这有点类似于函数的未知类型。(你不能使用unknown,因为它接受非函数。)


你的(...args: any) => unknown(...args: any[]) => unknown类型符合这些标准,但它们不是类型安全的,因为它们使用了不安全的any“类型”。在一个安全的类型系统中,顶级类型的值很容易提供,但很难消费。如果我想要一个unknown类型的值,你可以给我任何你想要的东西。但一旦我拥有它,如果不进一步检查它,我不知道该怎么处理它。如果我想要一个SomeFunction类型的值,你可以给我任何函数。所以一旦我拥有它,我可以像处理任意函数一样做任何事情,比如检查它的length属性或...嗯...不过,我不能做太多事情。我不能调用它,因为我不知道它将接受什么参数。因此,消耗SomeFunction将会很困难,或者至少很棘手。(...args: any) => unknown类型允许你以任何方式调用它,即使这会立即导致运行时错误:

type SomeFunction = (...args: any) => unknown;
const f: SomeFunction = (x: string) => x.toUpperCase();
f(); // 被错误接受,运行时错误!
f(1, 2, 3); // 被错误接受,运行时错误!

这就是为什么你不应该在这里使用any的原因。


假设你想要一个安全的SomeFunction版本,那么我们应该确保参数列表没有给定“无所不包”的类型。事实上,它应该是相反的。你应该完全无法调用它。这是因为函数类型在其参数类型上是逆变的(参见https://stackoverflow.com/q/66410115/2887218和关于--strictFunctionTypes编译选项的描述以获取更多信息)。在TypeScript中,这意味着顶级函数类型的参数列表应该有一个底部类型。在TypeScript中,底部类型是never类型

type SomeFunction = (...args: never) => unknown;
const f: SomeFunction = (x: string) => x.toUpperCase();
f(); // 在TS5.0及以上版本中会报错
f(1, 2, 3); // 报错

(...args: never) => unknown具有作为函数顶级类型的能力,主要是在microsoft/TypeScript#35438中实现的,完成于microsoft/TypeScript#52387,将随TypeScript 5.0发布。在此之前,即使不安全,也有可能调用上面的f()而不会报错,但其他调用会被拒绝。

代码示例链接

英文:

You want SomeFunction to be a top type for functions, where if you have a function f you can assign it to a variable of type SomeFunction, and if you have a non-function x then you can't assign it to SomeFunction. Sort of the unknown type for functions. (You can't use unknown because it accepts non-functions.)


Your (...args: any) => unknown or (...args: any[]) => unknown types meet these criteria, but they are not type safe because they use the unsafe any "type". In a safe type system, there is a tradeoff where values of top types are easy to supply but hard to consume. If I want a value of type unknown you can give me anything you want. But once I have it, I don't know what to do with it without inspecting it further. If I want a value of type SomeFunction, you can give me any function you want. So once I have it, I could do anything that works for an arbitrary function, such as inspecting its length property or... uh... well, there's not much I can do with it. I can't call it, because I don't know what arguments it would accept. So consuming a SomeFunction will be difficult, or at least tricky. The (...args: any) => unknown type allows you to call it however you want, even if this would immediately lead to a runtime error:

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!

That's the reason why you shouldn't use any there.


Assuming you want a safe version of SomeFunction, then we should make sure that the argument list isn't given an "anything-goes" type. Indeed, it should be the opposite. You should be completely unable to call it. That's because function types are contravariant in their parameter types (see https://stackoverflow.com/q/66410115/2887218 and a description of the --strictFunctionTypes compiler option for more information). In TypeScript, that means the top function type should have a bottom type for its parameter list. And in TypeScript, the bottom type is the never type:

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

This ability for (...args: never) => unknown to act as a function top type was implemented mostly in microsoft/TypeScript#35438 and completed in microsoft/TypeScript#52387, which will be released with TypeScript 5.0. Before then it's possible to call f() above without error (even though it is not safe), but other calls are rejected.

Playground link to code

huangapple
  • 本文由 发表于 2023年3月4日 01:03:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/75629945.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定