我在typescript中有一个抽象泛型类,它有一个带有类类型变量参数的泛型方法。我尝试在派生类中实现抽象方法,发现typescript编译器不检查派生方法中参数的类型。
下面是一个例子。我预计它在编译时会在Class1的进程方法上失败,因为参数类型错误。
我做错什么了吗?或者是故意的?或打字脚本编译器中的错误
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);
}
}
该行为不是bug。
TypeScript使用结构类型系统,因此,如果两个对象类型具有兼容的属性,即使这些类型具有不同的名称或来自不同的命名类/接口,它们也是兼容的。
注意,Customer
可分配给Product
,因为每个Customer
都具有number
值的id
属性和string
值的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);
}
}
或者编译器认为Customer
是Product
的一种特殊类型也没关系。在这种情况下,您可以不使用您的类型,我们可以检查为什么process()
不会导致编译器错误:
export class Class1 extends BaseClass<Customer> {
protected process(param: Product): void { // no error
console.log(param);
}
}
在这种情况下,BaseClass<Customer>
应该有一个接受Customer
的process()
方法。但是该CCD_ 20接受更宽类型的CCD_。这样可以吗?对因为如果process()
接受任何Product
自变量,那么它肯定接受任何Customer
自变量(因为Customer
是Product
的特殊类型,所以Class1
适当地扩展了BaseClass<Customer>
(。这演示了方法参数是如何反变的;子类方法可以接受比超类型上的相同方法更宽的参数。TypeScript确实允许方法参数是相反的,因此没有错误。
方法参数协变是不安全的(其中子类方法接受比它们各自的超类方法更具体的参数类型(,但包括TypeScript在内的一些语言允许这表示一些常见用例。也就是说,尽管TypeScript缺乏类型安全性,但它允许方法参数同时是反变量和协变的,也称为双变量。因此,如果你用另一种方式做,也不会有错误:
export class Class2 extends BaseClass<Product> {
protected process(param: Customer): void { // no error, bivariant
console.log(param);
}
}
概括一下:您可以将属性添加到Customer
和Product
,使它们在结构上不相关,也可以不使用它们,Class1.process()
将编译而不会出错。无论哪种方式,编译器都按预期运行。
希望能有所帮助。祝你好运