Depth Subtyping
Assume we have two classes, which have a subtype relationship using extends
:
1class Person {2 name: string;3}4class Employee extends Person {5 department: string;6}
It's valid to use an Employee
instance where a Person
instance is expected.
1class Person { name: string }2class Employee extends Person { department: string }3
4const employee: Employee = new Employee();5const person: Person = employee; // OK
However, it is not valid to use an object containing an Employee
instance
where an object containing a Person
instance is expected.
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.org/en/docs/faq/#why-cant-i-pass-a-string-to-a-function-that-takes-a-string-number. [incompatible-type]
This is an error because objects are mutable. The value referenced by the
employee
variable is the same as the value referenced by the person
variable.
person.who = new Person();
If we write into the who
property of the person
object, we've also changed
the value of employee.who
, which is explicitly annotated to be an Employee
instance.
If we prevented any code from ever writing a new value to the object through
the person
variable, it would be safe to use the employee
variable. Flow
provides a syntax for this:
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]
The plus sign +
indicates that the who
property is covariant.
Using a covariant property allows us to use objects which have subtype-compatible
values for that property. By default, object properties are invariant, which allow
both reads and writes, but are more restrictive in the values they accept.
Read more about property variance.