跳至主要內容

深度子類型

假設我們有兩個 類別,它們使用 extends 具有子類型關係

1class Person {2  name: string;3}4class Employee extends Person {5  department: string;6}

在預期 Person 實例的地方使用 Employee 實例是有效的。

1class Person { name: string }2class Employee extends Person { department: string }3
4const employee: Employee = new Employee();5const person: Person = employee; // OK

但是,在預期包含 Person 實例的物件中使用包含 Employee 實例的物件無效。

1class Person { name: string }2class Employee extends Person { department: string }3
4const employee: {who: Employee} = {who: new Employee()};5const person: {who: Person} = employee; // Error
5:31-5:38: Cannot assign `employee` to `person` because `Person` [1] is incompatible with `Employee` [2] in property `who`. This property is invariantly typed. See https://flow.nodejs.com.tw/en/docs/faq/#why-cant-i-pass-a-string-to-a-function-that-takes-a-string-number. [incompatible-type]

這是錯誤,因為物件是可變的。employee 變數引用的值與 person 變數引用的值相同。

person.who = new Person();

如果我們寫入 person 物件的 who 屬性,我們也已變更 employee.who 的值,而該值明確註解為 Employee 實例。

如果我們阻止任何程式碼透過 person 變數寫入新值到物件,則使用 employee 變數是安全的。Flow 提供了此語法的語法

1class Person { name: string }2class Employee extends Person { department: string }3
4const employee: {who: Employee} = {who: new Employee()};5const person: {+who: Person} = employee; // OK6person.who = new Person(); // Error!
6:8-6:10: Cannot assign `new Person()` to `person.who` because property `who` is not writable. [cannot-write]

加號 + 表示 who 屬性是 協變。使用協變屬性允許我們使用具有該屬性的子類型相容值的物件。預設情況下,物件屬性是不變的,允許讀取和寫入,但在它們接受的值方面有更多限制。

進一步了解 屬性變異