【问题标题】:Typescript: Get correct member type from super call argument in derived class打字稿:从派生类中的超级调用参数获取正确的成员类型
【发布时间】:2020-09-04 09:23:15
【问题描述】:

我有一个抽象类“Base”

abstract class Base {
  constructor(public readonly data: {
    [key: string]: BaseValue
  }) {}
}

还有一个派生类“Derived”:

class Derived extends Base {
  constructor() {
    super({
      test: SpecialValue
    } as const);
  }
}

SpecialValue 扩展 BaseValue

有没有办法在派生类中调用this.data["test"] 并将SpecialValue 作为类型? 我总是得到BaseValue 作为类型。

我不想要的: 在Base 上引入一个泛型类型,该类型的数据字段类型类似于class Derived extends Base<CorrectDataType>

【问题讨论】:

    标签: typescript


    【解决方案1】:

    具体而言,我将介绍以下定义,以便您的代码构成minimum reproducible example

    interface BaseValue {
        a: string;
    }
    interface SpecialValue extends BaseValue {
        b: number;
    }
    declare const SpecialValue: SpecialValue;
    

    需要注意的一点是,使用以下代码:

    const baseVals: {[k: string]: BaseValue} = { test: SpecialValue };
    

    编译器不知道也不能知道baseVals.testSpecialValue 类型。 {[k: string]: BaseValue} 的注解比这更宽,并且允许突变,比如

    baseVals.foo = { a: "hello" };
    baseVals.test = { a: "goodbye" };
    

    所以编译器不会仅仅因为它是用一个构造的,就推断Deriveddata 有一个test 类型为SpecialValue 的属性。如果你想让编译器知道这一点,你必须告诉它。

    一种方法是使Base 在其data 属性类型中具有通用性。但你说你不想那样。

    另一种方法是通过a declare property modifier 显式缩小Deriveddata 的类型:

    class Derived extends Base {
        declare readonly data: { test: SpecialValue };
        constructor() {
            super({
                test: SpecialValue
            } as const);
        }
    }
     
    

    这将产生你想要的效果,(但要小心......声明不是类型安全的;你可以declare readonly data: { oops: SomeOtherSubtypeOfBaseValue } 并且编译器不会抱怨super() 调用。)观察:

    const d = new Derived();
    d.data.test.b.toFixed(); // okay
    

    Playground link to code

    【讨论】:

    • 感谢您的深入回答。我认为编译器可以从构造函数参数中引入data 成员类型,然后通过超级调用中给出的参数来缩小这种类型。我认为as const 断言可以缩小足够的对象类型。 ..但我仍在学习并认为我还没有掌握打字稿类型系统的逻辑限制。
    【解决方案2】:

    您应该可以投射到SpecialValue。请记住,虽然 Typescript 允许这样做,但 linter 通常不允许这样做。

    const derivedValue: SpecialValue = <SpecialValue> this.data["test"]
    
    // or...
    
    const derivedValue: SpecialValue = this.data["test"] as SpecialValue
    

    编译器不够聪明,无法看到 this.data["test"] 被设置为 SpecialValue 否则。 Base 中的索引器试图保证所有值都是BaseValues,所以我认为这是编译器方面的合理行为。

    编辑:我刚刚看到 jcalz 对您的帖子的评论,这对于大多数情况来说绝对是更好的方法。

    【讨论】:

      猜你喜欢
      • 2015-04-16
      • 1970-01-01
      • 1970-01-01
      • 2020-10-23
      • 2022-11-03
      • 2021-10-27
      • 1970-01-01
      • 1970-01-01
      • 2020-05-31
      相关资源
      最近更新 更多