【问题标题】:How to exclude a class field from being inherited or somehow override it?如何排除类字段被继承或以某种方式覆盖它?
【发布时间】:2021-07-27 17:23:57
【问题描述】:

我有一个数据库模型 (Mongo) 和一个 API 模型 (Apollo)。除了从字符串解析为对象的“开始”字段外,这两者是相同的。如何排除“开始”字段被继承或以某种方式覆盖它?

class Start {
  iso: string;
  local: string;
}

// database model
class BaseThing {
  // a lot of other fields
  start: string; // < - - I don't want to have 'any' type here
}

// API model
class Thing extends BaseThing {
  // a lot of other fields [inherited]
  start: Start; // < - - how to exclude start from being inherited or somehow override it?
}

【问题讨论】:

    标签: typescript types model typegraphql class-transformer


    【解决方案1】:

    这不是很好的做法:子类继承超类的属性和方法。如果不需要继承,可能真的需要composition instead of class hierarchies,但这超出了这个问题的范围。让我们展示如何获得您正在寻找的行为,然后在最后提醒大家这可能会导致奇怪的问题。


    如果您想阻止编译器将 BaseThing 构造函数视为构造具有 start 属性的东西,您可以使用 type assertionBaseThing 的类型从(类似于)new () =&gt; BaseThingnew () =&gt; Omit&lt;BaseThing, "start"&gt; 使用Omit&lt;T, K&gt; utility type

    class Thing extends (BaseThing as new () => Omit<BaseThing, "start">) {
        start: Start = new Start(); // no error now
    }
    
    const thing = new Thing();
    console.log(thing.start.iso.toUpperCase()) // ISO
    

    这看起来有点难看,但它确实有效。编译器将Thing 视为从BaseThing 继承所有除了 start

    如果你经常做这种事情,你可以考虑将这种扩展重构为一个辅助函数,例如:

    class Thing extends OmitCtorProp(BaseThing, "start") {
        start: Start = new Start(); // still okay
    }
    

    在哪里

    const OmitCtorProp =
        <A extends any[], T extends object, K extends keyof T>(
            ctor: new (...a: A) => T, k: K) =>
            ctor as new (...a: A) => Omit<T, K>;
    

    可以隐藏在某个库中。


    请注意,根据基类的实现,这种故意扩大然后不兼容的重新缩小可能会导致麻烦。想象一下这样的事情:

    class BaseOops {
        val: string = "a string"
        method() {
            console.log(this.val.toUpperCase())
        }
    }
    
    const baseOops = new BaseOops();
    baseOops.method(); // "A STRING"
    

    到目前为止一切顺利。但后来:

    class SubOops extends OmitCtorProp(BaseOops, "val") {
        val: number = 12345;
    }
    
    const subOops = new SubOops();
    subOops.method(); // RUNTIME ERROR!  this.val.toUpperCase is not a function
    

    这会失败,因为基类的 method() 取决于 valstring 而不是其他任何东西。将val 更改为某些非string 类型的伪子类在运行时将不满意,除非它们也覆盖method()。因此,任何有意避开类型安全警告的人都应注意不要绊倒问题所在的地毯部分。

    Playground link to code

    【讨论】:

    • 非常感谢您的全面回答!我可能需要几个小时来思考你写的所有东西。你花在帮助我上的时间不会白费! :)
    猜你喜欢
    • 2019-03-31
    • 2016-02-01
    • 2012-07-28
    • 2012-11-20
    • 1970-01-01
    • 2015-08-26
    • 1970-01-01
    • 2021-11-30
    • 1970-01-01
    相关资源
    最近更新 更多