Перейти к содержанию

Совместимость объединений

Поскольку понимание поведения типа Union при проверке на совместимость может вызывать противоречия, эта совсем крохотная глава будет посвящена этому механизму.

Совместимость

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

Как известно к данному моменту, объектный тип A совместим с объектным типом B, если первый содержит все обязательные признаки второго. Кроме того члены, участвующие в проверке на совместимость, не обязаны принадлежать к идентичным типам, достаточно, чтобы они также были совместимы. Утрированно всё сказанное можно перефразировать как - тип, обладающий большим количеством совместимых признаков, совместим с типом, обладающим меньшим количеством признаков. Или даже - больший тип совместим с меньшим типом.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
interface Smaller {
  a: number;
  b: string;
}
interface Bigger {
  a: number;
  b: string;
  c: boolean;
}

declare let small: Smaller;
declare let big: Bigger;

let s: Smaller = big; // Ok
let b: Bigger = small; // Error

Любому разработчику, начавшему свою карьеру с языка, реализующего ооп парадигму, подобное поведение кажется само собой разумеющимся. Так вот, с типом объединение (Union) все в точности наоборот. Точнее может показаться, что наоборот, хотя на самом деле это совершенно другой случай.

1
2
3
4
5
6
7
8
type Smaller = number | string;
type Bigger = number | string | boolean;

declare let small: Smaller;
declare let big: Bigger;

let s: Smaller = big; // Error
let b: Bigger = small; // Ok

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

В нашем примере переменная с типом Bigger, помимо прочего, может быть ассоциирована со значением, принадлежащим к типу boolean, который не определяет множество типа Smaller.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
type Smaller = number | string;
type Bigger = number | string | boolean;

declare let small: Smaller;
declare let big: Bigger;

/**[0] */
let s: Smaller = big; // Error
/**[1] */
let b: Bigger = small; // Ok

/**
 * [0] переменная big может иметь значение, принадлежащие
 * к типу boolean, которое отсутствует в типе Smaller. Поэтому
 * переменная big не может быть присвоена переменной с Smaller.
 *
 * [1] И наоборот. Поскольку переменная small может иметь значение,
 * принадлежащее либо к number, либо string, её можно присвоить переменной
 * с типом Bigger, поскольку множество, определяющее его, включает данные типы.
 */

Комментарии