为什么 class 中的 属性 不是只读的(打字稿)

Why property in class is not read only (Typescript)

为什么 属性 name in class PeronsImpl 不是只读的,我们可以将创建的对象重新分配给它?

    interface Person {
    readonly name: string;
}

interface Greeting extends Person {
    greet(message: string): void;
}

class PersonImpl implements Greeting {
   name: string;
   age = 30;
   constructor(n: string) {
      this.name = n;
   }
   greet(message: string) {
      console.log(message + ' ' + this.name);
   }
} 

let pers = new PersonImpl("maha");
console.log(`pers : ${pers.name}`);
pers.name = "maha2";
pers.greet("hello"); //hello maha2

Why property name in class PeronsImpl is not read only...

因为它被类型转换为 PersonImpl 而不是 Person。如果你想 name 作为只读,你也必须在实现中做到这一点 or 变量 pers 应该被定义为类型 Person.

class PersonImpl implements Greeting {
   readonly name: string; // <-- added readonly
   age = 30;
   constructor(n: string) {
      this.name = n;
   }

或指定类型

let pers:Person = new PersonImpl("maha"); // <-- added :Person

这有两个部分:Class 属性不从 classes 或它们扩展的接口继承类型;和只读属性可以相互分配给可变属性,也可以从可变属性相互分配。


Class 属性不从 classes 或它们扩展的接口继承类型

当您编写 class Foo implements Bar {...} 时,编译器不会使用 Bar 来推断 Foo 的任何属性的类型。它的行为就好像 implements Bar 不存在一样,推断出 Foo 的所有类型的属性,然后只是 对照 Bar 检查 。例如:

interface Foo {
  x: 0 | 1;
}

class Bar implements Foo {
  x = 1; // error! x is inferred as number, not 0 | 1 
}

这是一个错误,因为 class Bar { x = 1 } 只会为 x 推断出 number,并且由于 number 不能分配给 0 | 1,所以这是一个错误.这种行为让很多人感到惊讶和不愉快,并且有很多 GitHub 问题在这里要求更好的东西。请参阅 microsoft/TypeScript#10570 了解一个特别长期未解决的问题。

这意味着 PersonImplname 属性 是 string 类型,而不是 readonly:

class PersonImpl implements Greeting {
  name: string; // string, not readonly string
  //...
}

只读属性可与可变属性相互赋值

第二个问题是,仅在属性 readonly 方面不同的类型被编译器认为是相互兼容的。您可以将 {readonly x: string} 分配给 {x: string},反之亦然,不会出错。请参阅 for more information about why that is, or see microsoft/TypeScript#13347 以了解要求对此进行更改的功能请求。

所以这意味着编译器不会发出 PersonImplname 属性 是可变的而 Greetingreadonly 的错误。这些类型不被认为是不兼容的。


把它们放在一起,你就会得到你在这里看到的怪异之处。您可能会愉快地假设 PersonImplname 属性 是 readonly,然后当您被允许为其赋值时会感到困惑。

这里的修复是 class 继承的打字问题的一般解决方法;为您的属性提供显式类型注释和修饰符

class PersonImpl implements Greeting {
  readonly name: string; // annotate with readonly
  /* ... */
}

let pers = new PersonImpl("maha");
pers.name = "maha2"; // error

Playground link to code