如何理解类型 `new (...args: any[]) => any`

11

我正在阅读class-validator库的代码,其中包含以下isInstance方法:

/**
 * Checks if the value is an instance of the specified object.
 */
isInstance(object: any, targetTypeConstructor: new (...args: any[]) => any) {
    return targetTypeConstructor
        && typeof targetTypeConstructor === "function"
        && object instanceof targetTypeConstructor;
}

对于如何理解类型new (...args: any[]) => any,你有什么想法吗?这是我第一次见到这种构造方式...

4个回答

32

让我们把类型缩小到更小的、更容易理解的部分,然后再重新组合成完整的内容。

首先,让我们忘掉 new,并集中注意力于定义的后半部分:

(...args: any[]) => any

接下来,让我们暂时忘记参数:

() => any

希望这是熟悉的函数返回类型 any

接下来,我们可以添加回参数:

(...args: any[]) => any

...args: any[] 使用 Rest Parameters 构造,它基本上表示可以有任意数量的参数提供给类型 any。因为有未知数量的 any 参数,所以参数的类型是一个 any 数组。

因此,现在希望这是一个接受任意数量的(类型为 any 的)参数,并返回类型为 any 的函数。

最后,我们可以加回 new 关键字,得到:

new (...args: any[]) => any

这里的 new 关键字指定了此函数可以被视为类构造函数,并使用 new 关键字调用。

这让我们看到了整个图景:该函数是一个接受任意数量(类型为any)参数的函数,返回any类型的结果,并能够使用new关键字作为构造函数。

从API的角度来看,这本质上允许您将任何类构造函数传递给该函数。


8

分解成几个部分:

new
这个关键字在TypeScript中指定了给定属性的构造函数应该是什么样子。详细解释请参见:https://dev59.com/WVkS5IYBdhLWcg3wn3--#39623422

(...args: any[]) => any
这种语法描述了一个函数类型(构造函数是一个函数)。

...
是ES6扩展操作符。它是将所有数组元素逐个列出的简写形式。

any[]
意味着args是一个数组,其元素可以是任何类型。

=> any
指定函数的返回类型。在这种情况下,它允许构造函数返回任何类型。


3
new (...args: any[]) => any

这种类型指定了一个函数,它接受任意数量的参数,这些参数属于any类型,返回值也是any类型,并且可以使用new关键字调用。
构造函数是一种特殊类型的函数,它在运行时强制要求使用new关键字进行调用,在TypeScript中,这可以被静态检测到,因此targetTypeConstructor指定了一个任意的构造函数作为isInstance()的第二个参数。
因此,检查似乎是多余的。
typeof targetTypeConstructor === "function"

由于 TypeScript 在编译时已经强制执行 targetTypeConstructor : new (...args: any[]) => any,因此如果传递的值为 null,则已经通过 targetTypeConstructor && ... 条件在运行时失效。这个条件是必要的,以防止在 ECMAScript 规范 §12.10.4 中的第 4 步 中,targetTypeConstructor 实际上为 null 时,object instanceof targetTypeConstructor 抛出 TypeError 的情况发生:

12.10.4 运行时语义:instanceof 运算符 (Vtarget

  1. 如果 Type(target)不是Object,则抛出 TypeError 异常。
  2. instOfHandler 为 ? GetMethod(target, @@hasInstance)。
  3. 如果 instOfHandler 不为 undefined,则

    a. 返回 ToBoolean(? Call(instOfHandler, target, « V »))。

  4. 如果 IsCallable(target)为 false,则抛出 TypeError 异常。
  5. 返回 ? OrdinaryHasInstance(targetV)。

注意事项

第4步和第5步提供了与以前的 ECMAScript 版本兼容的语义,这些版本未使用 @@hasInstance 方法来定义 instanceof 运算符的语义。如果对象没有定义或继承 @@hasInstance,则使用默认的 instanceof 语义。


1
具体来说,new (...) => ... 表示构造函数。 - Patrick Roberts
如果我理解正确,typeof targetTypeConstructor === function 的判断是多余的,因为最后的检查 object instanceOf targetTypeConstructor 如果 targetTypeConstructor 不是一个函数,则总是为 false...不知道是否更安全将其保留,以防 Java 运行时在未来某个时刻抛出异常...或者在某些运行时中过去发生了异常....? - Ole
我们难道不必考虑相应的Javascript运行时版本、function (object, targetTypeConstructor),以及在运行时情况下,也许targetTypeConstructor是其他类型而非函数(用户错误场景)吗? - Ole
用户错误将导致 TypeScript 编译错误 - 它永远不会到达运行时。 - Patrick Roberts
我同意你的建议,但是尽量让库与其他运行时兼容,因为我知道有很多人(React...)会直接使用没有任何TypeScript的ES。虽然我喜欢TypeScript,但我正在努力坚持使用它! - Ole
显示剩余4条评论

1

意味着参数targetTypeConstructor是一个接受参数并且可以用作构造函数(您可以使用new关键字创建实例)的函数。您可以传递一个简单的函数或者不是抽象类的类。

如果想了解更多,可以在Typescript Playground中查看示例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接