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

use*_*862 44 interface typescript

如何编写实现此TypeScript接口的类(并保持TypeScript编译器满意):

interface MyInterface {
    (): string;
    text2(content: string);
}
Run Code Online (Sandbox Code Playgroud)

我看到了这个相关的答案: 如何让一个类在Typescript中实现一个调用签名?

但这仅在接口仅具有裸功能签名时才有效.如果要实现其他成员(例如函数text2),它将不起作用.

bas*_*rat 43

类无法实现typescript接口中可用的所有内容.两个主要示例是可调用签名和索引操作,例如:实现可索引接口

原因是界面主要用于描述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;
}
);
Run Code Online (Sandbox Code Playgroud)

  • 没有任何方法可以做到这一点没有中间演员`any`?这样做,你实际上绕过了类型系统,你可以在没有从编译器得到任何错误的情况下将任何字面分配给`x`. (3认同)

Tom*_*ett 16

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

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

在最初询问和回答这个问题时,我认为在TypeScript中没有提供的交叉类型是获得正确输入的秘诀.

  • 在当前版本的TS中,它是`assign <T,U>(目标:T,来源:U):T&U` (4认同)

Sam*_*Sam 9

以下是对已接受答案的详细说明.

据我所知,实现调用签名的唯一方法是使用函数/方法.要实现其余成员,只需在此函数上定义它们即可.对于来自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
Run Code Online (Sandbox Code Playgroud)

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

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

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");
Run Code Online (Sandbox Code Playgroud)