【问题标题】:How would you define a type in typescript that's both an object and a function at the same time?您将如何在 typescript 中定义一个同时是对象和函数的类型?
【发布时间】:2018-02-02 08:02:07
【问题描述】:

在阅读this answer 到问题How is jQuery's $ a function and an object? 之后,我开始思考。你会如何在打字稿中定义这种类型?

在普通 JS 中这是完全有效的:

var f = function() { alert('yo'); }
f.foo = "bar";

alert(f.foo); // alerts "bar"
f();          // alerts "yo"

但是在 typescript 中,f.foo 会抛出错误,Property 'foo' does not exist on type '() => void'

虽然您可以使用bracket notation 获得类似的结果:

var f = function() { alert('yo'); }
f['foo'] = "bar";

alert(f['foo']); // alerts "bar"
f();          // alerts "yo"

这将完全绕过类型系统,以及 typescript 的类型安全。

有没有一种方法可以在不违反 typescript 的类型安全的情况下实现这种类型的功能?

【问题讨论】:

  • 也许你可以在DefinitelyTyped/types/jquery/index.d.ts 中找到提示,我认为他们确实这样做了,但我没有仔细研究。
  • @t.niese 他们会这样做,但是该文件有 8000 行,当我什至不知道自己在寻找什么时,提取定义此特定行为的几行变得更加困难。
  • 但这至少是一个起点;)。以防万一您没有得到任何有帮助的答案,您至少可以通过此链接找到解决方案。我还需要查看整个文档才能找到该信息,因为我也不知道。
  • @t.niese 是的,谢谢。希望我不必诉诸于此。
  • 你想要什么样的功能?某种工厂函数,如 jQuery 的 $,它获取 CSS 选择器并返回带有 jQ​​uery 函数的包装 DOM 对象?不清楚你想要什么样的功能。

标签: javascript typescript types prototype


【解决方案1】:

您可以使用& 定义具有多种类型的变量。它被称为“交叉类型”。更多信息在official docs

这是在您的示例中使用它的方法:

let f: Function & {foo?: String} = function() { alert('yo'); }
f.foo = "bar";

alert(f.foo); // alerts "bar"
f();          // alerts "yo"

Playground Link

这里,f 的类型是 Function & {foo?: String},因为它既是 Function,也是 {foo?: String}? 在这里是必需的,因为您将其初始化为仅Function,然后才将值分配给foo

您可以在 Playground 中看到代码已编译,甚至提供与您开始时相同的 JavaScript 代码,验证这是正确的语法。

【讨论】:

  • 使用Function 允许将任何函数签名分配给fplayground。 AFAIK 除了接口之外,无法指定具有给定签名的函数类型。
  • 另外,foo 被键入为可能未定义;这不是问题,因为 Typescript 的流分析会看到这个特定对象的分配,并确定 foostring 而不是 string | undefined。一旦在本地上下文之外(例如,如果从函数返回),并使用strictNullChecks 选项,foo 将被视为string | undefined。 (上面的游乐场链接)。
【解决方案2】:

您可以通过在接口上指定调用签名来定义这种类型:

interface MyFoo {
    (): void;
    foo: string;
}

您可以通过将函数转换为any 来初始化对象,然后再将其分配给变量:

let f: MyFoo = function() { alert('yo'); } as any;
f.foo = 'bar';
x();

Playground link


顺便说一句,JQueryStatic 类型(这是$ 变量的类型)在JQuery defintions 中的定义类似:

interface JQueryStatic<TElement extends Node = HTMLElement> {
    ...
    Deferred: JQuery.DeferredStatic;
    ...
    (html: JQuery.htmlString, ownerDocument_attributes: Document | JQuery.PlainObject): JQuery<TElement>;
    ...
    (selector: JQuery.Selector, context: Element | Document | JQuery | undefined): JQuery<TElement>;

(不能内联链接,所以放在这里)

但是,这并没有真正的帮助,因为您的问题也是如何使用这种类型初始化变量。 JQuery 库没有这个问题,因为它不是用 Typescript 编写的。

【讨论】:

  • 你会如何使用这个界面?
  • @torazaburo 公平地说,必须使用any 进行本应简单的初始化让我感到有些不舒服。
  • @ZevSpitz 谢谢。我同意,让它使用any 感觉很脏,但是可以选择使用接口而不是交叉类型,在我看来,这可以弥补这一点。
  • @Olian04 除了 1) 任何可分配给 Function 的函数,而不仅仅是特定的签名之外,为什么您更喜欢接口而不是交集类型;和 2) foo 属性是可选的,因此可能是 undefined(正如我在 cmets herehere 中指出的那样?
  • @ZevSpitz 除了这两个原因之外,这主要是个人喜好。感觉更容易维护,更自我记录。
猜你喜欢
  • 2020-10-09
  • 2015-02-05
  • 2016-12-02
  • 2019-10-06
  • 2022-10-30
  • 2020-04-18
  • 2020-07-01
  • 2021-05-17
  • 2023-03-28
相关资源
最近更新 更多