【问题标题】:Type safety issue when implementing abstract methods from a class using generics + union types [Typescript]使用泛型 + 联合类型从类实现抽象方法时的类型安全问题 [Typescript]
【发布时间】:2020-09-10 05:52:20
【问题描述】:

我正在尝试实现一个基类,它将字符串类型的并集作为泛型类型,然后在抽象方法中使用以这些字符串作为键的字典。

抽象方法的子实现在向其传递附加属性时不会抛出类型错误(但如果属性没有传递给它,则会正确抛出错误):

abstract class Parent<T extends string = string> {

    abstract doSomethingWithT(options: { [key in T]: string }): void;

}

class Child extends Parent<"foo" | "morefoo"> {

    /**
     * Would like to error on 'bar'
     */
    doSomethingWithT(arg: { foo: string; morefoo: string; bar: string }) {

    }

}

有没有在 Typescript 中实现这个功能的好方法?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    问题是options: { [k in T]: string } 确实匹配{ foo: string; morefoo: string; bar: string },因为它匹配{ foo: string; morefoo: string; } 并且类型的其余部分被忽略。要执行更严格的匹配,您可以执行以下操作:

    abstract class Parent<T extends string = string> {
    
        abstract doSomethingWithT(options: { [k in T]: string } & Record<string, never>): void;
    
    }
    
    class Child extends Parent<"foo" | "morefoo"> {
    
        doSomethingWithT(arg: { foo: string; morefoo: string; bar: string }) {
    
        }
    
    }
    

    never 确保如果你给它Record&lt;string, never&gt; 作为参数它会出错。如果你给它一个只匹配{ [k in T]: string } 的参数,它应该可以工作。

    不确定这可能会产生什么其他副作用:

    Playground link

    【讨论】:

      【解决方案2】:

      对于你所做的,你不需要泛型,因为它不会在子类中转换 options 的类型,因此它不会在这里带来任何利润。

      你可以摆脱它。要使用键轻松创建对象,您可以使用Record

      abstract class Parent {
      
          abstract doSomethingWithT(options: { [key: string]: string }): void;
      
      }
      
      class Child extends Parent {
      
          /**
           * Would like to error on 'bar'
           */
          doSomethingWithT(arg: Record<"foo" | "morefoo", string>) {
              console.log(
                  arg.foo,
                  arg.morefoo,
                  arg.bar, // error
              );
          }
      
      }
      
      const t = new Child();
      
      t.doSomethingWithT({
          foo: 'test',
          morefoo: 'test',
          bar: 'test', // error
          test: '123', // error
      });
      

      【讨论】:

      • 抱歉,我应该澄清一下 - 发布的示例是我所面临问题的简化摘录。在实际情况下,我想将一些常用方法放入使用泛型类型的抽象类中。
      • 那么您的代码没有问题。因为使用bar,它仍然适合父方法并满足其签名。这有点像父母方法说我需要 100 美元,孩子说我需要 150 美元,所以它花费 50 美元用于自己的需求,100 美元用于父母。
      猜你喜欢
      • 2018-10-29
      • 1970-01-01
      • 2012-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多