Spec-Zone.ru › Flow

Подмножества и подтипы

Что такое подтип?​

Тип, такой как number, boolean или string, описывает множество возможных значений. number описывает все возможные числа, поэтому отдельное число (например, 42) будет подтипом типа number. И наоборот, number будет надтипом типа 42.

Чтобы определить, является ли один тип подтипом другого, нужно рассмотреть все возможные значения обоих типов и выяснить, является ли множество значений одного из них подмножеством множества значений другого.

Например, если у нас есть TypeA, описывающий числа от 1 до 3 (объединение литеральных типов), и TypeB, описывающий числа от 1 до 5, то TypeA будет считаться подтипом TypeB, поскольку TypeA является подмножеством TypeB.

type TypeA = 1 | 2 | 3;
type TypeB = 1 | 2 | 3 | 4 | 5;

Рассмотрим TypeLetters, описывающий строки «A», «B», «C», и TypeNumbers, описывающий числа 1, 2, 3. Ни один из них не будет подтипом другого, поскольку каждый содержит совершенно отдельный набор значений.

type TypeLetters = "A" | "B" | "C";
type TypeNumbers =  1  |  2  |  3;

Наконец, если у нас есть TypeA, описывающий числа от 1 до 3, и TypeB, описывающий числа от 3 до 5, ни один из них не будет подтипом другого. Хотя оба содержат число 3 и описывают числа, каждый из них включает некоторые уникальные элементы.

type TypeA = 1 | 2 | 3;
type TypeB = 3 | 4 | 5;

Когда используются подтипы?​

Большая часть работы Flow заключается в сравнении типов друг с другом.

Например, чтобы определить, правильно ли вы вызываете функцию, Flow должен сравнить передаваемые вами аргументы с параметрами, которые ожидает функция.

Часто для этого нужно выяснить, является ли передаваемое значение подтипом ожидаемого значения.

Поэтому, если вы напишете функцию, ожидающую числа от 1 до 5, ей подойдёт любой подтип этого множества.

function f(param: 1 | 2 | 3 | 4 | 5) {
  // ...
}


declare const oneOrTwo: 1 |  2; // Subset of the input parameters type.
declare const fiveOrSix: 5 | 6; // Not a subset of the input parameters type.


f(oneOrTwo); // Works!
f(fiveOrSix); // Error!

Подтипы сложных типов​

Flow должен сравнивать не только множества примитивных значений: ему также необходимо уметь сравнивать объекты, функции и все остальные типы, используемые в языке.

Подтипы объектов​

Для начала можно сравнить два объекта по их ключам. Если объект содержит все ключи другого объекта, он может быть его подтипом.

Например, если у нас есть ObjectA с ключом foo и ObjectB с ключами foo и bar, то ObjectB может быть подтипом ObjectA, если ObjectA является неточным.

type ObjectA = {foo: string, ...};
type ObjectB = {foo: string, bar: number};


let objectB: ObjectB = {foo: 'test', bar: 42};
let objectA: ObjectA = objectB; // Works!

Но также нужно сравнить типы значений. Если у обоих объектов есть ключ foo, но один имеет тип number, а другой — string, то ни один из них не будет подтипом другого.

type ObjectA = {foo: string, ...};
type ObjectB = {foo: number, bar: number};


let objectB: ObjectB = { foo: 1, bar: 2 };
let objectA: ObjectA = objectB; // Error!

Если эти значения объекта сами являются объектами, их нужно будет сравнить друг с другом. Необходимо рекурсивно сравнивать каждое значение, пока не станет ясно, является ли один тип подтипом другого.

Подтипы функций​

Правила подтипизации функций сложнее. До сих пор мы видели, что A является подтипом B, если B содержит все возможные значения для A. Для функций неясно, как применить эту взаимосвязь. Для простоты можно считать, что тип функции A является подтипом типа функции B, если функции типа A можно использовать везде, где ожидается функция типа B.

Допустим, у нас есть тип функции и несколько функций. Какие из этих функций можно безопасно использовать в коде, который ожидает функцию заданного типа?

type FuncType = (1 | 2) => "A" | "B";


declare function f1(1 | 2): "A" | "B" | "C";
declare function f2(1 | null): "A" | "B";
declare function f3(1 | 2 | 3): "A";


f1 as FuncType; // Error
f2 as FuncType; // Error
f3 as FuncType; // Works!
  • f1 может возвращать значение, которое FuncType никогда не возвращает, поэтому код, рассчитанный на FuncType, может работать небезопасно, если использовать f1. Её тип не является подтипом FuncType.
  • f2 не может обработать все значения аргументов, которые обрабатывает FuncType, поэтому в коде, рассчитанном на FuncType, нельзя безопасно использовать f2. Её тип также не является подтипом FuncType.
  • f3 может принимать все значения аргументов, которые принимает FuncType, и возвращает только те значения, которые возвращает FuncType, поэтому её тип является подтипом FuncType.

В общем случае правило подтипизации функций таково: тип функции B является подтипом типа функции A тогда и только тогда, когда входные значения B образуют надмножество входных значений A, а выходные значения B — подмножество выходных значений A. Подтип должен принимать как минимум те же входные значения, что и родительский тип, и возвращать не более того же набора выходных значений.

Направление применения правила подтипизации к входным и выходным значениям определяется вариантностью, которой посвящён следующий раздел.

См. также​

  • Вариантность — взаимодействие подтипизации с параметрами обобщённых типов
  • Иерархия типов — наглядное представление взаимосвязей всех типов
  • Номинальная и структурная типизация — определение отношений подтипов с помощью классов и объектов

© 2013–present Facebook Inc.
Licensed under the MIT License.
https://flow.org/en/docs/lang/subtypes

Spec-Zone.ru

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