如何让一个类在 Typescript 中实现调用签名?

Posted

技术标签:

【中文标题】如何让一个类在 Typescript 中实现调用签名?【英文标题】:How to make a class implement a call signature in Typescript? 【发布时间】:2012-09-27 23:45:51 【问题描述】:

我在 typescript 中定义了如下接口:

interface MyInterface 
    () : string;

这个接口只是引入了一个不带参数并返回一个字符串的调用签名。如何在类中实现这种类型?我尝试了以下方法:

class MyType implements MyInterface 
    function () : string 
        return "Hello World.";
    

编译器一直告诉我

“MyType”类声明了“MyInterface”接口,但没有实现它:“MyInterface”类型需要调用签名,但“MyType”类型缺少调用签名

如何实现调用签名?

【问题讨论】:

为了它的价值,我filed an issue related to this。 在这种情况下,MyInterface 的行为有点像 typedef:它定义了一个始终返回字符串的函数。这是函数的子类型,可用于将类型分配给任何函数,不带参数并返回字符串。一种新型。类在 JS 中产生经典模式,调用它们产生 Object,所以类不能实现这个接口。基本上只有当它们不被称为构造函数(即使用 new)时才可以。 解决方案也有类似的问题:***.com/questions/13136892/overload-implementation/… 【参考方案1】:

类不能匹配那个接口。你能得到的最接近的,我认为是这个类,它将生成在功能上与接口匹配的代码(但不是根据编译器)。

class MyType implements MyInterface 
  constructor 
    return "Hello";
  

alert(MyType());

这将生成工作代码,但编译器会抱怨MyType 不可调用,因为它具有签名new() = 'string'(即使您使用new 调用它,它也会返回一个对象)。

要创建与界面完全匹配且编译器不会抱怨的东西,您必须执行以下操作:

var MyType = (() : MyInterface => 
  return function()  
    return "Hello"; 
  
)();
alert(MyType());

【讨论】:

那为什么不var MyType : MyInterface = function() return "hello";;呢?我认为 OP 的接口实际上是说实现者应该是一个函数。 @kahoon 我认为 OP 希望实现者是一个返回字符串的函数。 @PeterOlson 那么在接口定义中添加CallSignatures 有什么意义呢? @valentin 你可以拥有一个不仅仅是类的接口 实际上你不需要创建如此复杂的声明 :-) 我的意思是在这种情况下不需要嵌套匿名函数。【参考方案2】:

如果可调用接口应该有其他方法,你可以这样做:

interface Greeter 
    (): void;
    setName(name: string): void;


class ConsoleGreeter 

    private constructor( // constructable via `create()`
        private name = 'world'
    ) 

    public call(): void 
        console.log(`Hello $this.name!`);
    

    public setName(name: string) 
        this.name = name;
    

    public static create(): Greeter 
        const instance = new ConsoleGreeter();
        return Object.assign(
            () => instance.call(),
            
                setName: (name: string) => instance.setName(name)
                // ... forward other methods
            
        );
    


const greeter = ConsoleGreeter.create();
greeter(); // prints 'Hello world!'
greeter.setName('Dolly');
greeter(); // prints 'Hello Dolly!'

缺点:greeter instanceof ConsoleGreeterfalse

【讨论】:

您为什么决定在Object.assign 调用中编写所有函数调用程序?你为什么不直接写Object.assign(instance.call, instance); @RobertKoritnik 我这样做是为了简单起见。可以引入一个不需要转发每个实例方法的通用解决方案,但它不会像您建议的那么简单...... 1.需要将call方法绑定到instance,否则@987654328当您调用 call 时,@ 将是 undefined。 2.Object.assign复制一个源对象的所有可枚举的OWN属性,但instance通过原型链继承所有方法。 是的,当我尝试实现自定义 angularjs IHttpService 时,我也意识到了同样的情况,它也具有隐式调用签名。有趣的是Object.assign 的第一个参数是一个函数而不是一个对象。这几乎感觉就像创建一个带有静态成员的类(构造函数)(不是原型的一部分,而是直接构造函数的一部分)。【参考方案3】:

此答案中的代码示例假定以下声明:

var implementation: MyInterface;

提供可调用接口的实现

作为the accepted answer 的后续行动,正如它的一些评论者所建议的那样,匹配接口调用签名的函数隐式实现了接口。所以你可以使用任何匹配的函数作为实现。

例如:

implementation = () => "Hello";

您不需要明确指定函数实现接口。但是,如果您想明确,可以使用强制转换:

implementation = <MyInterface>() => "Hello";

提供可重用的实现

如果您想像通常使用 Java 或 C# 接口一样生成接口的可重用实现,只需将该函数存储在其使用者可访问的位置。

例如:

function Greet() 
    return "Hello";


implementation = Greet;

提供参数化实现

您可能希望能够以与参数化类相同的方式参数化实现。这是执行此操作的一种方法:

function MakeGreeter(greeting: string) 
    return () => greeting;


implementation = MakeGreeter("Hello");

如果您希望将结果类型化为接口,只需显式设置返回类型或强制转换返回的值即可。

【讨论】:

以上是关于如何让一个类在 Typescript 中实现调用签名?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 TypeScript 中实现睡眠功能?

在 Typescript 中实现接口时如何定义私有属性?

如何在 React TypeScript 中实现 e 和 props?

如何在 vue 组件中实现 typescript 代码?

如何在 TypeScript 中实现的接口中进行构造函数重载?

如何在TypeScript中实现redux中间件类