我在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);
}
}
答案 0 :(得分:1)
该行为不是错误。
TypeScript使用structural类型系统,因此两个对象类型具有兼容的属性就可以兼容,即使这些类型具有不同的名称或来自不同的命名类/接口也是如此。
请注意,Customer
可分配给Product
,因为每个Customer
都有一个number
值的id
属性和一个{{1} }值的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>
的{{1}}方法。但是,此process()
接受更广泛的类型Customer
。这样可以吗?是!因为如果process()
接受任何Product
参数,那么它肯定地 接受任何process()
参数(因为Product
是{{1 }},因此Customer
正确地扩展了Customer
)。这是方法参数如何contravariant的演示;与超类型上的同一方法相比,允许子类方法接受 wide 参数。 TypeScript确实允许方法参数是互变的,因此没有错误。
将方法参数设为covariant(在其中子类方法比其各自的超类方法接受更具体的参数类型而言)是不安全的,但是有些包括TypeScript 的语言允许它表示一些常见的用例。也就是说,尽管TypeScript缺乏类型安全性,但它允许方法参数既是协变的又是协变的,也称为bivariant。因此,如果您以其他方式完成此操作,那么也不会出现错误:
Product
回顾一下:您可以向Class1
和BaseClass<Customer>
添加属性,以使它们在结构上不相关,或者可以不理会它们,而export class Class2 extends BaseClass<Product> {
protected process(param: Customer): void { // no error, bivariant
console.log(param);
}
}
可以正确编译。无论哪种方式,编译器的行为均符合预期。
希望有帮助。祝你好运!