Подтипизация объектов
Типы объектов связаны друг с другом двумя взаимодополняющими правилами: подтипизацией по глубине, которая применяется к типам вложенных свойств, и подтипизацией по ширине, которая применяется к набору свойств объекта.
По умолчанию Flow использует более строгую вариативность, чем TypeScript. Изменяемые свойства объектов и изменяемые массивы инвариантны в Flow, но ковариантны в TS. Именно поэтому {x: number} не может небезопасно расшириться до {x: number | string}. Подробнее см. в разделе Сравнение вариативности в Flow и TypeScript.
Подтипизация по глубине
Предположим, у нас есть два класса, связанные отношением подтипизации с помощью extends:
class Person {
name: string;
}
class Employee extends Person {
department: string;
} Допустимо использовать экземпляр Employee там, где ожидается экземпляр Person.
class Person { name: string }
class Employee extends Person { department: string }
const employee: Employee = new Employee();
const person: Person = employee; // OK Однако недопустимо использовать объект, содержащий экземпляр Employee, там, где ожидается объект, содержащий экземпляр Person.
class Person { name: string }
class Employee extends Person { department: string }
const employee: {who: Employee} = {who: new Employee()};
const person: {who: Person} = employee; // Error Это ошибка, поскольку объекты изменяемы. Значение, на которое ссылается переменная employee, — то же самое значение, на которое ссылается переменная person.
class Person { name: string }
declare const person: {who: Person};
person.who = new Person(); Если мы запишем значение в свойство who объекта person, то также изменим значение employee.who, для которого явно указан тип экземпляра Employee.
Если запретить коду записывать новое значение в объект через переменную person, использовать переменную employee будет безопасно. В Flow для этого предусмотрен специальный синтаксис:
class Person { name: string }
class Employee extends Person { department: string }
const employee: {who: Employee} = {who: new Employee()};
const person: {readonly who: Person} = employee; // OK
person.who = new Person(); // Error! Ключевое слово readonly указывает, что свойство who является ковариантным. Ковариантное свойство позволяет использовать объекты, значения этого свойства которых совместимы по подтипу. По умолчанию свойства объектов инвариантны: их можно читать и изменять, но допустимые для них значения более ограничены.
Подробнее о вариативности свойств.
Подтипизация по ширине
Безопасно использовать объект с «дополнительными» свойствами там, где ожидается объект с определённым набором свойств, если тип этого объекта неточный.
function func(obj: {foo: string, ...}) {
// ...
}
func({
foo: "test", // Works!
bar: 42 // Works!
}); Внутри func мы знаем, что obj имеет как минимум свойство foo, а тип выражения доступа к свойству obj.foo будет string.
Этот вид подтипизации обычно называют «подтипизацией по ширине», поскольку тип «шире» (то есть имеет больше свойств), чем более узкий тип, и является его подтипом.
Таким образом, в следующем примере obj2 является подтипом obj1.
let obj1: {foo: string, ...} = {foo: 'test'};
let obj2 = {foo: 'test', bar: 42};
obj2 as {foo: string, ...}; Однако часто бывает полезно знать, что свойство точно отсутствует.
function func(obj: {foo: string, ...} | {bar: number, ...}) {
if (obj.foo) {
obj.foo as string; // Error!
}
} В приведённом выше коде возникает ошибка типов, потому что Flow также разрешил бы выражение вызова func({foo: 1, bar: 2}), поскольку {foo: number, bar: number} является подтипом {bar: number, ...}, одного из членов объединённого типа параметра.
В таких случаях, когда полезно указать на отсутствие свойства, можно использовать точные типы объектов.
function func(obj: {foo: string} | {bar: number}) {
if (obj.foo) {
obj.foo as string; // Works!
}
} Точные типы объектов отключают подтипизацию по ширине и не допускают наличия дополнительных свойств.
Использование точных типов объектов позволяет Flow знать, что во время выполнения дополнительных свойств не будет, благодаря чему уточнения типов становятся более точными.
См. также
- Вариативность — как ковариантность позволяет выполнять подтипизацию по глубине для свойств только для чтения
- Подтипы — общая теория подтипизации
- Объекты — свойства объектов только для чтения и только для записи
© 2013–present Facebook Inc.
Licensed under the MIT License.
https://flow.org/en/docs/lang/object-subtyping