前言
文中内容都是参考https://www.typescriptlang.org/docs/handbook/type-compatibility.html#handbook-content 内容。
类的类型兼容性
类与对象字面量和接口差不多,但有一点不同:类有静态部分和实例部分的类型。
在比较两个类型是否兼容时,除了遵照结构类型兼容规则(如果x要兼容y,那么y至少具有与x相同的属性。)
, 还需要注意以下几点:
- 只需比较类实例的属性和方法是否兼容即可;
- 静态成员和构造函数不会比较;
- 私有(private)、受保护(protected)的属性和方法,必须来自相同的类
举例进行分析:
class Animal {
feet: number;
constructor(name: string, numFeet: number) { }
}
class Size {
feet: number;
constructor(numFeet: number) { }
}
let a: Animal;
let s: Size;
a = s; // 没毛病
s = a; // 没毛病
以上代码中, 类Animal
与类Size
拥有共同的属性feet
, 即使它们的构造函数不同, 这两个类也是互相兼容。
类的私有成员和受保护成员
类的私有成员和受保护成员会影响兼容性。 当检查类实例的兼容时,如果目标类型包含一个私有成员,那么源类型必须包含来自同一个类的这个私有成员。 同样地,这条规则也适用于包含受保护成员实例的类型检查。 这允许子类赋值给父类,但是不能赋值给其它有同样类型的类。
举例说明:
class Animal {
private feet: number;
constructor(name: string, numFeet: number) { }
}
class Size {
private feet: number;
constructor(numFeet: number) { }
}
let a: Animal;
let s: Size;
a = s;
// 不能将类型“Size”分配给类型“Animal”。
// 类型具有私有属性“feet”的单独声明
当把 Size
赋值给 Animal
的时候,得到一个错误,说它们的类型不兼容。 尽管 Size
里也有一个私有成员 feet
,但它明显不是 Animal
里面定义的那个。
对于protected
成员也一样:
class Animal {
protected feet: number;
constructor(name: string, numFeet: number) { }
}
class Size {
feet: number;
constructor(numFeet: number) { }
}
let a: Animal;
let s: Size;
a = s;
// 不能将类型“Size”分配给类型“Animal”。
// 属性“feet”受保护,但类型“Size”并不是从“Animal”派生的类
因为类 Animal 中的成员 feet 是受保护的,所以不能赋值成功。
举例: 目标类型包含一个私有成员,源类型也包含来自同一个类的这个私有成员
class Animal {
private name: string;
constructor(theName: string ) { this.name = theName; }
}
class Dog extends Animal {
constructor() { super("Dog"); }
}
let a: Animal = new Animal('Bird');
let d: Dog = new Dog();
a = d; // 没毛病
d = a; // 没毛病 父类之所以能够给赋值给子类,是因为子类中没有成员
这个例子中有 Animal
和 Dog
两个类, Dog
是 Animal
类的子类。 我们创建了几个这些类的实例,并相互赋值来看看会发生什么。
因为 Animal
和 Dog
共享了来自Animal
里的私有成员定义 private name: string
,因此它们是兼容的。
前提也是需要符合成员的类型都是兼容的, 如果子类中有其他非静态属性或方法, 则父类赋值给子类会报错。
class Animal {
private name: string;
constructor(theName: string) {
this.name = theName;
}
}
class Dog extends Animal {
age: number;
constructor(age: number) {
super("Dog");
this.age = age;
}
}
let a: Animal = new Animal("Bird");
let d: Dog = new Dog(2);
a = d; // 没毛病
d = a; // Property 'age' is missing in type 'Animal' but required in type 'Dog'.
举例说明: 静态成员不会比较
class Animal {
private name: string;
constructor(theName: string) {
this.name = theName;
}
}
class Dog extends Animal {
static age: number = 1;
constructor() {
super("Dog");
}
static run() {}
}
let a: Animal = new Animal("Bird");
let d: Dog = new Dog();
a = d; // 没毛病
d = a; // 没毛病
Dog
类中有静态属性age
和静态方法run
, 但是比较两个类型是否兼容时, 静态成员和构造函数不会比较, 所以父子类型相互兼容。
以上ts代码均在 https://www.typescriptlang.org/play 上运行过,版本为4.7.2。
最后, 如有错误,欢迎各位大佬指点!感谢!
参考资料
https://www.typescriptlang.org/docs/handbook/type-compatibility.html#handbook-content