使用裸函数签名和其他字段实现 TypeScript 接口

Posted

技术标签:

【中文标题】使用裸函数签名和其他字段实现 TypeScript 接口【英文标题】:Implementing TypeScript interface with bare function signature plus other fields 【发布时间】:2013-05-06 16:23:46 【问题描述】:

我如何编写一个实现这个 TypeScript 接口的类(并让 TypeScript 编译器满意):

interface MyInterface 
    (): string;
    text2(content: string);

我看到了这个相关的答案: How to make a class implement a call signature in Typescript?

但这只有在接口只有裸函数签名时才有效。如果您有额外的成员(例如函数 text2)要实现,则它不起作用。

【问题讨论】:

***.com/questions/16248812/… 简短的回答是您链接的问题中提到的“类无法匹配该接口”***.com/a/12770145/390330 【参考方案1】:

一个类不能实现 typescript 接口中可用的所有内容。两个主要示例是可调用签名和索引操作,例如:Implement an indexible interface

原因是接口主要用于描述 javascript 对象可以做的任何事情。因此,它需要非常健壮。然而,TypeScript 类旨在以更 OO 传统/易于理解/易于键入的方式专门表示原型继承。

您仍然可以创建一个遵循该接口的对象:

interface MyInterface 
    (): string;
    text2(content: string);


var MyType = ((): MyInterface=>
  var x:any = function():string  // Notice the any 
      return "Some string"; // Dummy implementation 
  
  x.text2 = function(content:string)
      console.log(content); // Dummy implementation 
  
  return x;

);

【讨论】:

在没有any 的中间转换的情况下有什么方法可以做到这一点?这样做,您实际上绕过了类型系统,您可以将任何内容分配给x,而不会从编译器中收到任何错误。【参考方案2】:

使用 ES6 的 Object.assign 有一种简单且类型安全的方法:

const foo: MyInterface = Object.assign(
  // Callable signature implementation
  () => 'hi',
  
    // Additional properties
    text2(content)  /* ... */ 
  
)

当最初提出和回答这个问题时,我认为 TypeScript 中不提供交叉类型,这是正确输入的秘诀。

【讨论】:

这不是很安全的类型,因为Object.assign 返回any 在当前版本的 TS 中是assign<T, U>(target: T, source: U): T & U @Flavien 当前签名如下:github.com/Microsoft/TypeScript/blob/master/lib/… 从 typescript 2.3 开始,这将返回正确的类型,而不使用“任何”或变异对象。在我看来,当前版本的 TS 是最好的方法。 肯定是变异对象(第一个参数)。自 TS 2.0 以来,打字一直是正确的。【参考方案3】:

这是the accepted answer的详细说明。

据我所知,实现调用签名的唯一方法是使用函数/方法。要实现其余成员,只需在此函数上定义它们。对于来自 C# 或 Java 的开发人员来说,这可能看起来很奇怪,但我认为这在 JavaScript 中很正常。

在 JavaScript 中,这很简单,因为您只需定义函数然后添加成员即可。但是,TypeScript 的类型系统不允许这样做,因为在此示例中,Function 没有定义 text2 成员。

所以要达到你想要的结果,你需要在函数上定义成员的时候绕过类型系统,然后你可以将结果转换为接口类型:

//A closure is used here to encapsulate the temporary untyped variable, "result".
var implementation = (() => 
    //"any" type specified to bypass type system for next statement.
    //Defines the implementation of the call signature.
    var result: any = () => "Hello";

    //Defines the implementation of the other member.
    result.text2 = (content: string) =>  ;

    //Converts the temporary variable to the interface type.
    return <MyInterface>result;
)(); //Invokes the closure to produce the implementation

请注意,您不需要使用闭包。您可以在与生成的接口实现相同的范围内声明您的临时变量。另一种选择是命名闭包函数以提高可读性。

这是我认为更现实的例子:

interface TextRetriever 
    (): string;
    Replace(text: string);


function makeInMemoryTextRetriever(initialText: string) 
    var currentText = initialText;
    var instance: any = () => currentText;
    instance.Replace = (newText: string) => currentText = newText;

    return <TextRetriever>instance;


var inMemoryTextRetriever = makeInMemoryTextRetriever("Hello");

【讨论】:

以上是关于使用裸函数签名和其他字段实现 TypeScript 接口的主要内容,如果未能解决你的问题,请参考以下文章

为啥 TypeScript 中没有重载的构造函数实现?

Typescript - 具有通用类型函数的索引签名

Typescript - 在保留签名的同时包装函数

typeScript面对对象篇二

TypeScript 类方法具有与构造函数相同的重载签名

p1和p7签名的区别