【问题标题】:Can you extend a function in TypeScript?你可以在 TypeScript 中扩展一个函数吗?
【发布时间】:2019-05-21 14:59:49
【问题描述】:

我帮助维护一个 JavaScript 库,该库生成间谍函数,允许您检查传入函数的调用方式(主要用于单元测试)。

该库创建一个函数,该函数具有允许您检查调用的附加属性。

是否可以创建一个 TypeScript 定义,允许将函数传递给需要函数并具有额外属性的方法?

这是无效的,但类似于:

class Spy extends function {
    wasCalled: () => boolean;
    ...
}

这将允许我将间谍传递给具有此签名的函数:

function subjectUnderTest(callback:() => void) {
    ...
}

【问题讨论】:

    标签: typescript


    【解决方案1】:

    是的,the TypeScript handbook calls this a "hybrid type",因为它是函数类型和常规接口的组合。

    interface Spy {
        (foo: string, bar: number) : boolean; // Just an example
        wasCalled() : boolean;
    }
    
    var spy : Spy = createASpySomehow();
    var result = spy("foo", 123);
    if (spy.wasCalled()) {
        // ...
    }
    

    【讨论】:

    • 不知道为什么这被否决了,这正是我想要的。
    • 也许是因为createASpySomehow() 暗示的部分答案。我们如何通过扩展函数来创建接口 Spy 的实例?答案中链接的 Typescript 文档使用了不同的方法:将函数转换为接口,然后向函数添加属性,请参阅我的答案以获取示例。但是,即使我的回答也没有扩展功能。
    • @nalply 最初的问题是关于为现有的 JavaScript 库编写 TypeScript 类型定义。当然,如果您也在 TypeScript 中编写库 implementation,那么您将需要在创建函数的位置进行显式转换。 :-)
    【解决方案2】:

    我也想用一个函数扩展一个类,并制定了一个仅限 TypeScript 的解决方案。我不确定这是否是一个好主意,因为聪明的解决方案并不总是好的解决方案。 YMMV。

    感谢 Mattias Buelens 提供部分答案!我正在构建它。

    // same as in the answer of Mattias
    interface Spy {
        (foo: string, bar: number): boolean // Just an example
        wasCalled(): boolean
    }
    
    // and now for the real solution!
    class Spy {
        _wasCalled: boolean
        _baz: boolean // Just an example
    
        private constructor(baz: boolean) {
            this._wasCalled = false
            this._baz = baz
        }
    
        wasCalled(): boolean {
            return this._wasCalled
        }
    
        toString() { return '[object Spy]' }
    
        static create(baz: boolean) {
            const f = <Spy>function(this: Spy, foo: string, bar: number): boolean {
                // Do your thing here. Use f instead of this!
                console.log('wasCalled', f.wasCalled())
                f._wasCalled = true
            }
            const spy = new Spy(baz)
            Object.assign(f, spy)
            Object.setPrototypeOf(f, Spy.prototype)
    
            return f
        }
    }
    

    这个想法是创建一个函数和Spy的实例,然后将原型和属性都分配给函数。从静态方法返回实例。一个好处是toString() 方法。

    const spy = Spy.create(true)
    console.log('calling spy', spy('foo', 42))
    console.log('instanceof', spy instanceof Spy)
    

    按预期工作。

    我不认为new Spy() 会起作用,因为我们需要分配给一个函数,而不是反过来。而且因为我们不能替换 this 我们不能使 this 成为可调用的。我看到的一种假设方法是用真正的函数构造函数扩展一个类,如下所示:class Spy2 extends function() {} {},但我没有找到让它工作的方法。

    【讨论】:

    • 有趣的解决方案,但也许有点太神奇了。例如:由于您将函数的原型替换为Spy.prototype,因此您将丢失spy.bind()spy.apply()spy.call() 之类的方法。我认为直接在函数上添加额外的方法更容易,而不是尝试将它们放在函数的原型上。
    • 我同意...此解决方案的一个优点是您可以拥有一个复杂的构造函数,并且在此期间您将获得 TypeScript 的类型验证。
    猜你喜欢
    • 2016-08-16
    • 2011-11-18
    • 2011-09-27
    • 2010-10-21
    • 2011-06-14
    • 2017-05-14
    • 2018-12-01
    • 2011-10-10
    相关资源
    最近更新 更多