【问题标题】:Declare interface of class only with methods signature without names仅使用不带名称的方法签名声明类的接口
【发布时间】:2019-05-24 05:59:38
【问题描述】:

假设我有一个包含许多方法的类,但我确定它们的签名匹配。

是否可以描述这个类的接口而不在里面描述这个类的具体方法呢?喜欢这里:

interface IController {
  (input: string): number // any method without reference to its name
}

class Controller implements IController {
  method1(input: string): number { ...do something }
  method2(input: string): number { ...do something }
  ...
}

还是不可能?

【问题讨论】:

  • 如果不给方法命名,TypeScript 将无法判断对该方法的调用是否符合类型规则。
  • 仅供参考,您使用的语法声明了函数的call signature,而不是对象/类实例的函数属性/方法。

标签: javascript node.js typescript


【解决方案1】:

拥有索引签名的选项(正如@fk82 在他的回答中概述的那样)具有强制您向类添加索引签名的不良后果。这意味着你的类可以被任意字符串索引,这可能不是你想要的。

如果您的目标只是强制实现类仅具有具有给定签名的方法,则更好的选择是使用映射类型:

type IController<K extends PropertyKey> = { 
    [P in K]: (input: string) => number;
}

class Controller implements IController<keyof Controller> {
    method1(input: string): number { return input.length; }
    method2(input: string): number { return input === '' ? 0 : 1; }
}

let a = new Controller();
a['aa'] // not allowwed under no implicit any 

这有一个额外的好处,允许类有一些不符合签名的方法,如果需要,但是以明确的方式:

class Controller implements IController<Exclude<keyof Controller, 'special'>> {
    method1(input: string): number { return input.length; }
    method2(input: string): number { return input === '' ? 0 : 1; }
    special() { }
}

【讨论】:

    【解决方案2】:

    您可以使用index signature

    interface IController {
      [name: string]: (input: string) => number;
    }
    

    需要注意的是,TypeScript 编译器现在将要求您将索引签名添加到每个实现 IController 的类。 IE。您需要按如下方式定义您的 Controller 类:

    class Controller implements IController {
        [name: string]: (input: string) => number;
        method1(input: string): number { return input.length; }
        method2(input: string): number { return input === '' ? 0 : 1; }
    }
    

    这是一个带有完整示例的TS playground。请注意,索引签名将在断言中进行测试,例如

    const A = {
        m(input: string): number { return input.length; },
    } as IController;
    
    const B = {
        m(input: string): string { return input; }
    } as IController;
    

    并且B 的赋值会因为string 返回值而引发类型错误。

    【讨论】:

      【解决方案3】:

      根据@FK82 的回答,您可以破解某些东西以适应您寻求的解决方案,但这会破坏接口构造的目的,即将对象绑定到一堆编译时已知签名。在引用接口时,编译器如何知道您具体指的是什么方法?

      但是,据我所知,与其尝试填充基于接口的解决方案,为什么不在执行代码中声明一个功能抽象呢?只需描述函数签名并按照您的要求换成正确的方法,因为在 JS/TS 中,函数是一等公民。

      type Strategy = (input: string) => number;
      
      class Controller implements IController {
          method1(input: string): number { ...do something }
          method2(input: string): number { ...do something }
      }
      
      function useMethod(f: Strategy): any {
        ...
        const i = f('my string');
        ...
      }
      
      function main() {
          const c = new Controller ();
          const method = chooseOne === true ? c.method1 : c.method2;
          useMethod (method);
      }
      

      这种做事方式与 OOP 中的 Strategy Pattern 并没有太大的不同,但是 FP 解决方案更精简,并且拥有我认为 Javascript/Typescript 的更好特性之一。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-01-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多