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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16508435/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-11 01:04:35  来源:igfitidea点击:

Implementing TypeScript interface with bare function signature plus other fields

interfacetypescript

提问by user1147862

How do I write a class that implements this TypeScript interface (and keeps the TypeScript compiler happy):

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

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

I saw this related answer: How to make a class implement a call signature in Typescript?

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

But that only works if the interface only has the bare function signature. It doesn't work if you have additional members (such as function text2) to be implemented.

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

采纳答案by basarat

A class cannot implement everything that is available in a typescript interface. Two prime examples are callable signatures and index operations e.g. : Implement an indexible interface

类不能实现打字稿接口中可用的所有内容。两个主要示例是可调用签名和索引操作,例如:实现可索引接口

The reason is that an interface is primarily designed to describe anything that JavaScript objects can do. Therefore it needs to be really robust. A TypeScript class however is designed to represent specifically the prototype inheritance in a more OO conventional / easy to understand / easy to type way.

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

You can still create an object that follows that interface:

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

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;
}
);

回答by Tom Crockett

There's an easy and type-safe way to do this with ES6's Object.assign:

使用 ES6 有一种简单且类型安全的方法可以做到这一点Object.assign

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

Intersection types, which I don't think were available in TypeScript when this question was originally asked and answered, are the secret sauce to getting the typing right.

当最初提出和回答这个问题时,我认为在 TypeScript 中不可用的交集类型是正确打字的秘诀。

回答by Sam

Here's an elaboration on the accepted answer.

这是对已接受答案的详细说明。

As far as I know, the only way to implement a call-signature is to use a function/method. To implement the remaining members, just define them on this function. This might seem strange to developers coming from C# or Java, but I think it's normal in JavaScript.

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

In JavaScript, this would be simple because you can just define the function and then add the members. However, TypeScript's type system doesn't allow this because, in this example, Functiondoesn't define a text2member.

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

So to achieve the result you want, you need to bypass the type system while you define the members on the function, and then you can cast the result to the interface type:

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

//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

Note that you don't need to use a closure. You could just declare your temporary variable in the same scope as the resulting interface implementation. Another option is to name the closure function to improve readability.

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

Here's what I think is a more realistic example:

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

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");