首先咱们要分明 private
、 protected
现阶段只是javascript
中的保留字(Reserved words
),而非关键字(Keywords
)。因而TypeScript
中的纯类型申明语句,编译后都会被擦除。
class Person {
public name: string;
protected age: number;
private isMarried: boolean;
}
//编译后果
class Person {
}
TypeScript是一个构造类型语言。当比拟两个不同的类型时,不论它们来自哪里,如果所有成员的类型都是兼容的,那么就说这些类型自身是兼容的。
interface Named {
name: string;
}
class Bar {
name: string;
}
class Foo {
name: string;
}
// OK, because of structural typing
let a: Named = new Person(); //✔️
let b: Foo = new Bar(); //✔️
因为 TypeScript
属性申明默认是 public
,所以下面能够以 b.name
模式拜访,而java
则默认是protected
。
然而,当比拟具备 private
成员或 protected
成员的类型时,会区别对待这些类型。如果其中一种类型具备private成员,那么另一种类型必须具备来源于同一处申明的private成员。这同样实用于protected成员。
class Bar {
private name: string;
}
class Foo {
private name: string;
}
let bar: Bar = new Foo(); // ❌
//Type 'Foo' is not assignable to type 'Bar'.
//Types have separate declarations of a private property 'name'.
下面的这些概念规定来源于 TypeScript Handbook,这里只是做个简要的引子。
TypeScript
在判断类型兼容时,为什么解决 private
、protected
的规定要有别于 public
, 这到底有什么潜在的益处。
假如有这样一个场景,目前电动汽车尚且处于倒退的初级阶段,汽车品牌特斯拉、蔚来的最大里程数 maxMileage
值一样。
interface Car {
maxMileage: number;
}
class Tesla implements Car {
maxMileage: number = 500;
}
class Nio implements Car {
maxMileage: number = 500;
}
function drive(car :Tesla) {
console.log(car.maxMileage)
}
let tesla = new Tesla();
let nio = new Nio();
drive(tesla); // ✔️
drive(nio); // ✔️
因为TypeScript
是结构式语言,因Tesla
、Nio
又有着雷同名称、类型的字段 maxMileage
,即便 drive
入参申明为 Tesla
类型,也能通过校验。目前而言,即便误用,drive
的体现一样,不会有问题,但随着技术的倒退,两个品牌的 maxMileage
值将不一样,drive
的行为也将千差万别。这个bug将始终潜伏着,直到引起重大故障才会引起关注。
在上例根底上减少1) 2) 两处,多了 private
(protected
亦可) 申明的 brand
属性,来解决构造一样,但又想辨别类型的场景,达到相似申明式类型零碎的成果。这里就是利用了private
、protected
属性必须源于同一处申明才可断定类型兼容。
class Tesla implements Car {
private brand: string = "Tesla"; // 1)
maxMileage: number = 500;
}
class Nio implements Car {
private brand: string = "Tesla"; //2)
maxMileage: number = 500;
}
function drive(car :Tesla) {
console.log(car.maxMileage)
}
let tesla = new Tesla();
let nio = new Nio();
drive(tesla); // ✔️
drive(nio); // ❌
//Argument of type 'Nio' is not assignable to parameter of type 'Tesla'.
//Types have separate declarations of a private property 'brand'.
//编译后
class Tesla {
constructor() {
this.brand = "Tesla";
this.maxMileage = 500;
}
}
class Nio {
constructor() {
this.brand = "Tesla";
this.maxMileage = 500;
}
}
尽管达到了咱们想要的成果,但类实例会多出 brand
属性,减少了运行时开销,如果这不是你想要的,能够如下解决:
class Tesla implements Car {
//@ts-ignore
private brand: string;
maxMileage: number = 500;
}
class Nio implements Car {
//@ts-ignore
private brand: string ;
maxMileage: number = 500;
}
//编译后
class Tesla {
constructor() {
this.maxMileage = 500;
}
}
class Nio {
constructor() {
this.maxMileage = 500;
}
}
能够看到编译后的代码很污浊了。 //@ts-ignore
仅在 strictPropertyInitialization: true
时须要,防止因未初始化属性而编译报错。
Types have separate declarations of a private property
报错还会呈现在类extends继承的时候。初看很奇怪,应用姿态不同,但报错信息且相似。
class ElectricVehicle {
private charge() {};
}
//Type 'FF91' is not assignable to type 'ElectricVehicle'.
// Types have separate declarations of a private property 'charge'
class FF91 extends ElectricVehicle { // ❌
private charge() {};
}
通过将 private
改成 protected或public
能够修复。很多文章会提到这是因为 private
语义上是公有的,对子类不可见,所以不能进行笼罩,而protected
、public
语义上就是对子类可见的,子类晓得以后在进行笼罩行为,这只是一方面。
咱们假如 TypeScript
容许笼罩 private
办法,下面的类申明编译通过。但当咱们执行上面语句时,下面的报错再次出现。
let parent = new ElectricVehicle();
let child = new FF91();
parent = child; // ❌
//Type 'FF91' is not assignable to type 'ElectricVehicle'.
// Types have separate declarations of a private property 'charge'
最后的示例,Foo、Bar
只是两个构造相似的类,并无继承关系,断定类型不兼容尚可了解。这里父子类之间类型不兼容就没法自圆了。
所以编译器提前在类申明时就报错,防止延后到应用阶段。这也是为什么 FF91
类申明继承时的报错信息和后面的一样。
示例 Playground
发表回复