javascript TypeScript“unknown”不允许函数参数中有非未知类型

qmelpv7a  于 2023-01-01  发布在  Java
关注(0)|答案(2)|浏览(283)

为什么不管用?

const x: unknown[] = ['x', 32, true]; // OK
const y: (...args: unknown[]) => unknown = (xx: number) => {}; // ERROR

// Type '(xx: number) => void' is not assignable to type '(...args: unknown[]) => unknown'.
//  Types of parameters 'xx' and 'args' are incompatible.
//    Type 'unknown' is not assignable to type 'number'. ts(2322)

我的目标是确保y是任何可运行的函数。我试图不使用any。希望提高我对unknown在这种情况下如何工作的理解。

js5cn81o

js5cn81o1#

函数类型在其参数类型中是 * 逆变 * 的;更多细节参见Difference between Variance, Covariance, Contravariance and Bivariance in TypeScript,convariance表示可赋值性的方向翻转;如果T可赋值给U,则(...u: U) => void可赋值给(...t: T) => void,反之则不可。这是类型安全所必需的。请描绘数据流的方向:如果你想要水果,我可以给你一个苹果,但是如果你想要吃你所有水果的东西,我不能给你只吃苹果的东西。
函数类型(xx: number) => void等价于(...args: [number]) => void,你不能把它赋值给(...args: unknown[]) => void。是的,[number]可以赋值给unknown[],但这不是我们关心的方向,因此你的赋值是不安全的。如果这样做有效:

const y: (...args: unknown[]) => unknown =
    (xx: number) => xx.toFixed(); // should this be allowed?

这样,你就可以用任何一组参数调用y(),而不会出现编译器错误,但却遇到了一个运行时错误:

y("x", 32, true); // no compiler error
// 💥 error! xx.toFixed is not a function

将输入参数列表扩展到unknown[]会使函数类型变得非常窄,因为大多数函数并不接受所有可能的参数列表。
因此,如果你真的想要一个类型,任何函数都应该被赋值给它,你需要将输入参数列表 * 缩小 * 到一个不能接受 * 任何 * 输入的类型,如下所示:

type SomeFunction = (...args: never) => unknown;
const y: SomeFunction = (xx: number) => xx.toFixed(); // okay
// const y: SomeFunction

这是因为SomeFunction本质上是不可调用的(当然,它应该是不可调用的; ms/TS#48840有一个未解决的bug).如果我向你请求一个我不会调用的函数,你可以放心地交给我任何函数.相反,如果你交给我一个函数,我不知道它接受什么参数,我最好不要尝试调用它.
这是可行的,但是......它有点没用。一旦你有了y,你就不能用它做任何事情:

y(123); // error, oops doesn't know about its argument types anymore

对于您问题中的用例,我想这很好,因为您将这些不可调用的函数传递给非TypeScript代码,而这些代码不了解或不考虑我们的类型安全规则。
不过,对于其他可能正在阅读的人来说,* 验证 * 一个值可以被赋给一个类型而不 * 扩展 * 它可能会更有用,所以我们可以使用satisfies操作符来检查它是否符合该类型,而不是将y注解为SomeFunction

const y = ((xx: number) => xx.toFixed()) satisfies SomeFunction;
// const y: (xx: number) => string

它会编译(但是如果你写的是const y = "oops" satisifies SomeFunction,就会失败),而y仍然是(xx: number) => string,所以你仍然可以称之为:

y(123); // okay

Playground代码链接

osh3o9ms

osh3o9ms2#

为什么不管用?
下面是一个错误:

const x: unknown[] = ['x', 32, true]; // OK
const y: (...args: unknown[]) => unknown = (xx: number) => {}; // ERROR

因为这是一个错误的相同原因:

const x: unknown = 123; // OK 
const y: number = x; // ERROR: cannot assign unknown to number

简化原因:如果不先检查unknown的运行时值,就不能将其赋给其他任何对象。

相关问题