Spec-Zone.ru › Flow

Подтипизация объектов

Типы объектов связаны друг с другом двумя взаимодополняющими правилами: подтипизацией по глубине, которая применяется к типам вложенных свойств, и подтипизацией по ширине, которая применяется к набору свойств объекта.

Сравнение с TypeScript

По умолчанию 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API