TypeScript 值以Function类型输入时,不应可调用,

o3imoua4  于 4个月前  发布在  TypeScript
关注(0)|答案(9)|浏览(65)

Bug报告

🔎 搜索词

内置函数、接口、可调用、构造器、narrow、不安全

🕗 版本与回归信息

这个问题至少可以追溯到3.3.3版本。

  • 我尝试了所有版本,并查阅了关于函数的常见问题解答条目。

⏯ Playground链接

带有相关代码的Playground链接

💻 代码

type OnlyCallable = (...args: any) => any;
type OnlyConstructorCallable = new (...args: any) => any
type AnyFunction = OnlyCallable | OnlyConstructorCallable

function call(func: AnyFunction) {
  // @ts-expect-error Makes sense
  func();
  // @ts-expect-error Makes sense
  new func();

  // @ts-expect-error Makes sense
  const a: OnlyCallable = func;
  // @ts-expect-error Makes sense
  const b: OnlyConstructorCallable = func;

  // Why is this not an error?
  const c: Function = func;

  a();
  // @ts-expect-error Makes sense
  new a();

  new b();
  // @ts-expect-error Makes sense
  b();

  // Why is this not an error?
  c();
  // But this is?
  new c();
}

🙁 实际行为

Function 似乎是一个比 (...args: any) => any 更不安全的版本,因为它允许将 AnyFunction 赋值给它。

🙂 预期行为

Function 应该表现得像 (...args: any) => anyAnyFunction,甚至像 OnlyCallable & OnlyConstructorCallable。更糟糕的是,

function foo(value: unknown) {
  if (typeof value === 'function') {
    // `value` is narrowed to `Function`
    value();
  }

  if (value instanceof Function) {
    // `value` is narrowed to `Function` 
    value();
  }
}

// @ts-expect-error Makes sense
Promise()

// No type error but this will throw at runtime
foo(Promise)
lo8azlld

lo8azlld1#

这是预期的行为;我们使用的术语是“未指定类型的函数调用”。可以添加一个标志来关闭此功能,但这种行为可以追溯到TypeScript 0.8,而且我还没有看到关于它的很多反馈。

ztigrdn8

ztigrdn82#

让我觉得允许无类型函数可调用的启发式是有道理的,因为绝大多数“函数”都可以在不使用“new”的情况下调用。我主要反对的是允许上面定义的 OnlyConstructorCallableAnyCallable 被分配给 Function 。这种错误类别的感觉类似于通过将 ReadonlyArray 分配给 Array 而允许的错误类别。

我认为禁止这样的赋值不会影响到很少的用户,同时也会减少 Function 的特殊、神奇类型。实现这一点的最简单方法是将 Function 定义为 (...args: any) => any ,但我觉得这一定在之前就被提出过了。有没有好的例子说明这样做是不理想的?

zu0ti5jz

zu0ti5jz3#

Function 作为一个可调用对象的有效赋值目标是不可协商的部分。Function 本身与常规函数的关系与 stringString ,numberNumber 等的关系相同。

w8f9ii69

w8f9ii694#

Function 是一个有效的可调用对象的赋值目标
new () => T 在没有标准调用签名的情况下,技术上来说并不是可调用的,尽管如此 - 例如,Promise(); 在没有 new 的情况下会抛出异常,而且在没有实际尝试调用它的情况下,没有办法知道这一点(仅将某物作为 Function 类型输入)。

7fhtutme

7fhtutme5#

构造函数仍然具有 bind / call / apply,而 Function 的作用是提供这些功能。

iugsix8n

iugsix8n6#

根据我所知,在类构造函数上调用这些方法总是会导致运行时错误。你需要使用 Reflect.construct() 来调用类的构造函数。

1aaf6o9v

1aaf6o9v7#

是的,对于那些肯定存在但肯定不可调用的东西(DOM中的非用户可调用的HTML元素构造函数也存在这个问题),如何进行推理确实令人困惑。从技术上讲,你可以调用ctor.bind.bind(...,所以它就在那里...

eyh26e7m

eyh26e7m8#

我认为我已经说服自己,当前状态是可以接受的(好吧,至少没有比其他事情更糟糕)。既然这是允许的:

function foo(numberArray: number[]) {
  // IIRC this works because methods are bivariant (will #48240 allow more control over this?)
  const numberOrStringArray: Array<number | string> = numberArray;
  numberOrStringArray.push('hello');
}

我想调用和不调用 new 之间的不对称仍然让我有些困扰,但我不认为允许使用 Function 调用 new 会带来太多实际好处。
请随意关闭或保留以供讨论。

mzmfm0qo

mzmfm0qo9#

据我所知,在类构造函数上调用这些方法总是会导致运行时错误。.bind 可以与类构造函数安全地一起使用。示例:

class Foo {
   constructor(a) { console.log(a); }
}
const BoundFoo = Foo.bind(null, 'bound');

new BoundFoo()

相关问题