在派生 class 中更正扩展构造函数参数的 TypeScript 类型

Correct TypeScript types for extended constructor parameter in derived class

我有一个带有 BaseParams 构造函数参数的 Base class,我想从 Base 扩展一个 Derived class,它接受一个 ExtendedParams 构造函数参数,并将它与一些默认值一起传递给 super()。问题是我似乎找不到 Derived 构造函数参数的访问修饰符和类型的正确组合。

这是我目前的情况:

interface BaseParams {
  first: string;
}

interface ExtendedParams extends BaseParams {
  last?: string;
}

class Base {
  constructor(protected params: BaseParams) {}
  inspect(): void {
    console.log(this.params);
  }
}

class Derived extends Base {
  constructor(??? params ???) {
    super({ first: 'John', last: 'default', ...params });  // override default params with specified ones
    this.params.last;  // #1 - this should not generate any compile errors
    this.params.whatever = 'Wrong';  // #2 - TS should flag this as not existing on type ExtendedParams
  }
}

const d = new Derived({ first: 'Mike' });
d.inspect();  // #3 - should output { first: 'Mike', last: 'default', whatever: 'Wrong' }

我尝试将 Derived params 声明为 protected。这解决了#1 和#2。

class Derived extends Base {
  constructor(protected params: ExtendedParams) {
    super({ first: 'John', last: 'default', ...params });
    this.params.last;  // #1 - ok, no error
    this.params.whatever = 'Wrong';  // #2 - ok, flagged: `whatever` doesn't exist on type ExtendedParams
  }
}

问题是 last: 'default' 赋值没有发生,因为 protected 生成一个 this.params = params 赋值覆盖传递给 [=19= 的 params 值], 所以 d.inspect() 只输出 { first: 'Mike', whatever: 'Wrong' }.

我尝试省略 params 的访问修饰符。这导致正确的输出 { first: 'Mike', last: 'default', whatever: 'Wrong' },但也导致 this.params.last 被标记为错误,does not exist on type 'BaseParams'.

class Derived extends Base {
  constructor(params: ExtendedParams) {
    super({ first: 'John', last: 'default', ...params });
    this.params.last;  // #1 - wrong ly flagged 
    this.params.whatever = 'Wrong';  // #2 - ok, flagged
  }
}

是否有一些 TypeScript 魔术来通知编译器在 Derived class 中,this.paramsExtendedParams 类型, 默认值被传递给 super() 调用并且 this.params 之后没有被覆盖?

但是从 Derived 你访问 Base.params 显然是 BaseParams.

如果您希望 params 属性 更改派生 类 中的类型,您需要通用基类型:

// <P = BaseParams> allows to instantiate Base with BaseParams as default type.
class Base<P = BaseParams> {
    constructor(protected params: P) {}
    /* ... */
}

class Derived extends Base<ExtendedParams> {
    constructor(params: ExtendedParams) {
        super({ first: 'John', last: 'default', ...params });  // override default params with specified ones
        this.params.last;                // OK
        this.params.whatever = 'Wrong';  // Wrong
    }
}