【问题标题】:Limit a class to include only functions with a specific signature将类限制为仅包含具有特定签名的函数
【发布时间】:2018-12-04 03:11:51
【问题描述】:

在我的应用程序中,我有几个用于创建 XML 字符串的类。 每个类都有一些方法,它们接受一些参数并返回一个字符串。我想指定此限制,以便无法添加具有不同签名或其他类成员的方法。为此,这是我到目前为止的目标:

interface IRequestParser {
  [funcName: string]: (...args: any[]) => string;
}

还有一个类的例子:

class myParser implements IRequestParser {
  [funcName: string]: (...args: any[]) => string;

  func1(a) {
    return '';
  }
}

这会阻止我添加不返回字符串或任何其他非方法成员的函数,因此这些在 myParser 中是不允许的:

  a: string; // not allowed
  b() { // not allowed
    return 5;
  }

但是,这样可以让我从 myParser 的实例中调用任何函数名称而不会发出警报:

const a = new myParser();
console.log(a.func1('a'));
console.log(a.func2(4, 5, ['s', 'b']));

func1 的调用是有意义的,如果我没有为其参数a 提供值,我也会收到错误消息。但是,我也可以调用不存在的函数func2 和任何其他名称。

有没有办法既限制类成员的类型,又避免调用任何名称的任何函数的情况?

【问题讨论】:

    标签: javascript typescript


    【解决方案1】:

    你也可以这样做:

    class MyParser implements Record<keyof MyParser, (...args: any[]) => string> {
    
      // okay
      func1(a: any) {
        return '';
      }
    
      // error: string is not assignable to (...args: any[]) => string
      a: string; 
    
      // error: number is not assignable to string
      b() { 
        return 5;
      }
    }
    
    const myParser = new MyParser();
    myParser.func1("okay"); // works
    myParser.funcNope("huh"); // error, no property funcNope
    

    允许实现一个具体的映射类型(如Record&lt;&gt;),有时可能会有循环引用(通过keyof 引用键通常是可以接受的)。

    希望有所帮助;祝你好运!

    【讨论】:

    • 这看起来更直接,并且给出了更明确的错误信息。 Record 类型是我所缺少的。我会接受的。
    • 我对自己有点不高兴,因为我没有在implements 子句中使用keyof。您可以这样做是有道理的,因为在检查类之后才检查实现,因此应该知道键。很好的答案:)
    • 就我个人而言,我仍然对如何用自己的keyof 定义类型感到困惑,通常你不能使用它自己定义一个类型,以便它是递归的。有什么地方可以让我了解更多信息吗?
    • 我注意到在从MyParser 继承的类中,限制被取消了,有没有办法防止这种情况发生?
    • 不,它不会向下传播。如果你真的需要,你可能想回到像[whoKnows: string]: undefined | ((...args: any[]) =&gt; string) 这样的索引签名。这确实存在myParser.funcNope 本身不会是错误的问题,但至少它的类型包括undefined(假设您确实应该使用--strictNullChecks)所以如果尝试调用myParser.funcNope(),您将收到错误.
    【解决方案2】:

    您使用的接口允许类被任何字符串索引。我们无法用接口表达你想要的约束(至少我想不到,也许别人有想法)。

    您可以使用函数将类限制为仅具有带有签名的函数,同时保留类具有的实际属性:

    type ParserFn = (...args: any[]) => string;
    function createRequestParserClass<
      T extends new (...a: any[]) => Record<keyof InstanceType<T>, ParserFn>>(ctor: T) {
      return ctor
    }
    const myParser = createRequestParserClass(class {
      // a: string; // not allowed
      // b() { // not allowed
      //   return 5;
      // }
    
      func1(a: string) {
        return '';
      }
    });
    

    type myParser = InstanceType

    const a = new myParser();
    console.log(a.func1('a'));
    console.log(a.func2(4, 5, ['s', 'b'])); // error now 
    console.log(a.func1(1)); // also error
    

    【讨论】:

    • 我会测试一下。如果我想在某处定义类,并在其他地方创建一个实例,那么我可以在文件中定义内部类,然后我应该调用createRequestParserClass而不是创建它。也许将构造函数设为私有并将此函数添加到每个类以防止创建原始类?
    • @RanLottem 您可以像使用任何其他类一样使用 const myParser。结合myParser 类型(我在原始答案之后添加),两者应该几乎可以作为常规函数定义工作。如果你想在另一个文件中同时使用myParser ,那么它应该可以工作
    • 似乎在调用createRequestParserClass时尝试添加错误类型的类成员会导致错误,而不是在内部类的定义中(这是我正在寻找的“接口扩展”方法我试过)。不过,我从这个解决方案中学到了很多东西,谢谢。
    • @RanLottem 是的,错误将在函数调用而不是类本身上报告。我个人认为这并不重要,错误就是错误,但如果这使该解决方案对您无法使用,则取决于您。
    • @RanLottem 昨晚没有看到 jcalz 的答案,他肯定更好:-)
    猜你喜欢
    • 2019-03-29
    • 2012-10-18
    • 1970-01-01
    • 1970-01-01
    • 2019-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-25
    相关资源
    最近更新 更多