共计 3189 个字符,预计需要花费 8 分钟才能阅读完成。
首先咱们要分明 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