打字稿中的抽象泛型类

时间:2018-12-19 00:52:48

标签: typescript

我在typescript中有一个抽象的泛型类,该类具有带有类类型变量的参数的泛型方法。我试图在派生类中实现抽象方法,但发现Typescript编译器不检查派生方法中参数的类型。

这里是一个例子。我希望它在编译时在 Class1 process 方法上失败,因为参数类型错误。

我做错什么了吗?还是设计使然?还是打字稿编译器中的错误

class Product {
  id: number;
  name: string;
}

class Customer {
  id: number;
  name: string;
  address: string;
}

export abstract class BaseClass<TParam> {
  protected abstract process(param: TParam): void;
}

export class Class1 extends BaseClass<Customer> {
  protected process(param: Product): void {
    console.log(param);
  }
}

1 个答案:

答案 0 :(得分:1)

该行为不是错误。

TypeScript使用structural类型系统,因此两个对象类型具有兼容的属性就可以兼容,即使这些类型具有不同的名称或来自不同的命名类/接口也是如此。

请注意,Customer可分配给Product,因为每个Customer都有一个number值的id属性和一个{{1} }值的string属性。反之则不正确; name无法分配给Product,因为并非每个Customer都具有必需的Product属性。

这是一个错误吗?编译器认为addressCustomer的一种特殊形式对于您的代码是否有问题?如果是这样,解决此问题的最简单方法是为每种类型添加一个属性,编译器可以使用该属性来区分它们。例如:

Product

现在,代码将根据需要为您提供一个错误:

class Product {
  id!: number;
  name!: string;
  type?: "product" 
}

class Customer {
  id!: number;
  name!: string;
  address!: string;
  type?: "customer"
}

或者编译器认为export abstract class BaseClass<TParam> { protected abstract process(param: TParam): void; } export class Class1 extends BaseClass<Customer> { protected process(param: Product): void { // error! // ~~~~~~~ <-- Type 'Customer' is not assignable to type 'Product'. console.log(param); } } Customer的一种特殊形式也可以。在这种情况下,您可以不理会类型,我们可以检查为什么Product不会导致编译器错误:

process()

在这种情况下,export class Class1 extends BaseClass<Customer> { protected process(param: Product): void { // no error console.log(param); } } 应该具有接受BaseClass<Customer>的{​​{1}}方法。但是,此process()接受更广泛的类型Customer。这样可以吗?是!因为如果process()接受任何Product参数,那么它肯定地 接受任何process()参数(因为Product是{{1 }},因此Customer正确地扩展了Customer)。这是方法参数如何contravariant的演示;与超类型上的同一方法相比,允许子类方法接受 wide 参数。 TypeScript确实允许方法参数是互变的,因此没有错误。

将方法参数设为covariant(在其中子类方法比其各自的超类方法接受更具体的参数类型而言)是不安全的,但是有些包括TypeScript 的语言允许它表示一些常见的用例。也就是说,尽管TypeScript缺乏类型安全性,但它允许方法参数既是协变的又是协变的,也称为bivariant。因此,如果您以其他方式完成此操作,那么也不会出现错误:

Product

回顾一下:您可以向Class1BaseClass<Customer>添加属性,以使它们在结构上不相关,或者可以不理会它们,而export class Class2 extends BaseClass<Product> { protected process(param: Customer): void { // no error, bivariant console.log(param); } } 可以正确编译。无论哪种方式,编译器的行为均符合预期。

希望有帮助。祝你好运!