【问题标题】:How to represent the type of a value in Typescript?如何在 Typescript 中表示值的类型?
【发布时间】:2021-02-09 10:03:37
【问题描述】:

如何在 Typescript 中表示任意类型的值?例如:

class Checker {
  /**
   * @param first a value
   * @param second the type of a value
   * @return true if "first" is an instance of type "second"
   */
  public instanceOf(first: unknown, second: Class): boolean {
    return first instanceof second;
  }
}

以上代码的行为必须与first instanceof second 相同,但我不知道second 必须具有什么类型才能使其工作。

这个问题与Is there a type for "Class" in Typescript? And does "any" include it? 有关,但没有一个答案适用于具有私有构造函数的类(例如LocalDate)。我不能简单地将构造函数更改为 public,因为用户必须能够从第三方库中传递值。

什么不起作用

  • new (...args: never[]) => unknown) 失败:

不能将“私有”构造函数类型分配给“公共”构造函数类型。

  • public instanceOf<T>(first: unknown, second: T) 不起作用,因为它允许用户将非类型值传递给second,例如1234"test"。请记住,如果first instanceof 1234 导致编译器错误,那么instanceOf(first, 1234) 也应该如此。

  • public instanceOf<T extends object>(first: unknown, second: T) 阻止非类型值,但方法实现无法编译:

TS2359:“instanceof”表达式的右侧必须是“any”类型或可分配给“Function”接口类型的类型。

【问题讨论】:

  • 你可以只使用泛型吗?像这样:public instanceOf<T>(first: unknown, second: T): boolean;
  • new (...args: never[]) => unknown) 是表示类的方式。 provate 构造函数的全部意义在于您不能让其他任何人调用它,因此您将无法将私有构造函数获取到构造函数引用中。考虑公开
  • @TitianCernicova-Dragomir 用户需要能够传入任意类型(由第三方库定义),所以我不能简单地将构造函数更改为 public。
  • @TitianCernicova-Dragomir 使用泛型似乎破坏了实现。查看更新后的问题。

标签: typescript


【解决方案1】:

我认为 Function 类型在这里可能是正确的(因为该值从未被调用,而只是传递给 instanceof),但如果你不想使用它,你可以接受任何具有形状的东西{prototype: {constructor: any}}:

interface Classlike {
  prototype: {constructor: any}
}

class Checker {
  /**
   * @param first a value
   * @param second the type of a value
   * @return true if "first" is an instance of type "second"
   */
  public instanceOf(first: unknown, second: Classlike): boolean {
    return first instanceof second.prototype.constructor;
  }
}

Playground link

【讨论】:

  • 非常酷的想法。出于好奇,您所建议的first instanceof secondfirst instanceof second.prototype.constructor 有什么区别?
  • 从功能上讲,除了 Typescript 知道构造函数是 any,它知道用 instanceof 进行测试是有效的,但我们可以传递一个接口形状,以防止非类传递类型检查。这更像是对 Typescript 的让步,而不是其他任何事情;我认为仅将 Function 指定为类型是更好的方法。
【解决方案2】:

据我所知,Function 是唯一的出路:

class Checker {
  /**
   * @param first a value
   * @param second the type of a value
   * @return true if "first" is an instance of type "second"
   */
  public instanceOf(first: unknown, second: Function): boolean {
    return first instanceof second;
  }
}

eslint 警告:

避免使用 Function 类型,因为它提供的安全性很低,原因如下:

  • 它在调用值时不提供类型安全,这意味着很容易提供错误的参数。
  • 它接受类声明,调用时会失败,因为它们是在没有 new 关键字的情况下调用的。

我们可以做得更好吗?

【讨论】:

  • 我们能做得更好吗?”如何更好? Function 有什么问题?您可以仍然拥有从构造函数而不是 class 构造创建的对象。
  • @VLAZ 我的意思是,我们可以使用另一个没有 eslint 文档中提到的问题的构造吗?第二个警告似乎不适用于我的情况(因为我只调用instanceof)但是第一个警告呢?
  • 第一项和第二项都与这里无关。您想要一个构造函数(类或函数之一)并调用它。因此,您不会提供任何参数。由于您没有调用它,new 也无关紧要。实际上(再次)您希望同时传入一个类和一个构造函数。这里两者之间没有真正的区别——在这两种情况下,它只是一种函数。您可以将second 设为(...args: any[]) => void,这也将接受任何功能。这是多余的,但可能会避免 linter 错误。
  • @VLAZ 听起来您只是在提供任意函数的形状。使用这个比Function 有优势吗?后者更具可读性。
  • Function相比没有优势。好吧,除了避免 linter 错误之外,就是这样。我什至会说 linter 在这种情况下是错误的,因为Function 一般来说是不安全的,但在这种情况下它实际上是正确的类型 - 你想接受任何功能。你不会调用它,所以签名或是否是一个类是无关紧要的。所有这一切都由Function 很好地处理,因此我最初的问题是它有什么问题。如果 lint 错误是唯一的问题,我建议为这段代码取消它而不是更改代码。
猜你喜欢
  • 2020-05-23
  • 1970-01-01
  • 2016-03-22
  • 2022-01-05
  • 2013-09-21
  • 1970-01-01
  • 2019-12-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多