【问题标题】:typescript infer type from constructor argument called by sub classtypescript 从子类调用的构造函数参数推断类型
【发布时间】:2021-05-30 03:50:31
【问题描述】:

我有一个带有一些属性/方法的类,这些属性/方法返回最初通过构造函数传递的值:

class Base {
  constructor(arg) {
     this.options = arg.options;
  }
}

人们建议使用泛型作为打字稿等价物的此类:

class Base<T extends {options: any}> {
    options: T["options"];

    constructor(arg: T) {
        this.options = arg.options;
    }    
}

// works fine with instantiation
const inst = new Base({options: {foo: 123}});
inst.options.foo++; // ok
inst.options.bar; // TS Error

现在我有一个子类,它调用超类的构造函数并传递这些选项:

class Sub extends Base { // TS Error
    constructor() {
        super({options: {foo: 123}});
        this.options.foo++; // TS Error
    }
}

Typescript 编译器对此并不满意,并希望通过该类型扩展基类。但后来我有两次信息,在超级调用和扩展符号中。解决我的问题的正确 ts 语法是什么?

(typescript playground 中的代码)

【问题讨论】:

  • 您的游乐场链接被切断,因此代码被切断。它只是问题中的代码吗?如果是这样,我已经解决了。如果没有,请将代码同时添加到操场和问题中。 :-)
  • 你传递的值 super 真的是编译时文字吗?
  • FWIW,我怀疑你可以让 TypeScript 从 super 调用中推断出 Base 的类型参数。我怀疑{options: {foo: number}} 和{options: {foo: 123}} 的近乎重复是不可避免的。
  • @T.J.Crowder,我修复了链接。是的,该值在代码中是固定的,不会在运行时读取/构建。它实际上定义了这个特定类具有哪些特征,并且超类在 ctor 中提供了这个参数来动态生成这些特征。 (如果这有意义的话)当通过 new 关键字调用 Base 类时,TS 能够以某种方式提取一些类型信息。所以我希望在扩展课程时同样的逻辑可以工作。但它只需要对 ts 代码进行一些更改。

标签: typescript typescript-generics


【解决方案1】:

编辑:

问题不在于super(),而在于extends Base。无论如何,typescript 必须推断 Base 的泛型类型,而您不能指望 typescript 从对 super() 的调用中推断出这一点。


一开始我会建议这样做

    class Sub<T extends {options: any}> extends Base<T> {
    constructor() {
        super({options: {foo: 123}})
    }}

但这在a concrete instance is not allowed to be assigned to a type parameter 之后将不起作用。如果我们改为这样做,它会起作用

    class Sub<T extends {options: any}> extends Base<T> {
    constructor(arg: T) {
        super(arg)
    }}

const inst2 = new Sub({options: {foo: 123}})
inst2.options.foo++; // ok   

我认为正确的解决方案是简单地这样做

    class Sub extends Base<{options: any}> {
    constructor() {
        super({options: {foo: 123}})
    }}

但是,还是有问题。

const inst2 = new Sub();
inst2.options.foo++; //options is any which makes sense, we said that options is any above

你知道我们为什么使用泛型吗?当我们事先不知道类型是什么时。由于在这种情况下我们确切地知道类型是什么,所以我们可以安全地这样做:

class Sub extends Base<{ options: {foo: number} }> {
    // or even class Sub extends Base<{ options: {foo: 123} }> {
    constructor() {
        super({options: {foo: 123}});
        this.options.foo++;
    }
}

const inst2 = new Sub();
inst2.options.foo++;

这样做可以吗?当然,如果我们将选项以外的东西传递给Base,就会触发警告。所以我们满足Base的要求,同时确保我们传递给super()的东西和我们所说的泛型类型是一致的。

【讨论】:

    猜你喜欢
    • 2022-01-26
    • 2019-02-07
    • 2021-02-26
    • 2020-10-17
    • 1970-01-01
    • 1970-01-01
    • 2019-09-03
    • 1970-01-01
    相关资源
    最近更新 更多