【问题标题】:Abstract generic class in typescript打字稿中的抽象泛型类
【发布时间】:2018-12-19 09:23:25
【问题描述】:

我在打字稿中有一个抽象泛型类,它有一个带有类类型变量参数的泛型方法。我尝试在派生类中实现抽象方法,发现打字稿编译器不检查派生方法中的参数类型。

这是一个例子。我希望它在 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);
  }
}

【问题讨论】:

    标签: typescript


    【解决方案1】:

    该行为不是错误。

    TypeScript 使用structural 类型系统,因此如果两个对象类型具有兼容的属性,则它们是兼容的,即使类型具有不同的名称或来自不同的命名类/接口。

    然后请注意,Customer 可以分配给 Product,因为每个 Customer 都有一个 number-valued id 属性和一个 string-valued name 属性。反过来是不正确的。 Product 不能分配给Customer,因为不是每个Product 都具有必要的address 属性。

    这是一个错误吗?编译器认为Customer 是一种特殊的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);
      }
    }
    

    或者编译器认为CustomerProduct 的一种特殊类型也可以。在这种情况下,您可以不理会您的类型,我们可以检查为什么 process() 不会导致编译器错误:

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

    在这种情况下,BaseClass&lt;Customer&gt; 应该有一个接受Customerprocess() 方法。但是这个process() 接受更广泛的类型Product。可以吗?是的!因为如果process() 接受任何Product 参数,那么它肯定 接受任何Customer 参数(因为CustomerProduct 的一种特殊类型,因此Class1 正确扩展BaseClass&lt;Customer&gt;)。这是对方法参数如何contravariant 的演示;允许子类方法接受比超类型上的相同方法更宽 的参数。 TypeScript 确实允许方法参数是逆变的,因此没有错误。

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

    export class Class2 extends BaseClass<Product> {
      protected process(param: Customer): void { // no error, bivariant
        console.log(param);
      }
    }
    

    回顾一下:您可以向CustomerProduct 添加属性以使它们在结构上不相关,或者您可以不理会它们,Class1.process() 将编译而不会出错。无论哪种方式,编译器都按预期运行。

    希望对您有所帮助。祝你好运!

    【讨论】:

    • 谢谢你这么详细的解释。我期望像 C# 中的行为
    猜你喜欢
    • 2017-09-15
    • 2020-10-11
    • 1970-01-01
    • 2021-11-26
    • 2019-01-16
    • 1970-01-01
    • 1970-01-01
    • 2019-07-06
    • 2020-05-25
    相关资源
    最近更新 更多