Совместимость объединений¶
Поскольку понимание поведения типа Union
при проверке на совместимость может вызывать противоречия, эта совсем крохотная глава будет посвящена этому механизму.
Совместимость¶
Чтобы было проще понять суть противоречий, возникающих у разработчиков при понимании механизма совместимости типов объединение, стоит начать с повторения совместимости объектных типов.
Как известно к данному моменту, объектный тип A
совместим с объектным типом B
, если первый содержит все обязательные признаки второго. Кроме того члены, участвующие в проверке на совместимость, не обязаны принадлежать к идентичным типам, достаточно, чтобы они также были совместимы. Утрированно всё сказанное можно перефразировать как - тип, обладающий большим количеством совместимых признаков, совместим с типом, обладающим меньшим количеством признаков. Или даже - больший тип совместим с меньшим типом.
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
) все в точности наоборот. Точнее может показаться, что наоборот, хотя на самом деле это совершенно другой случай.
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
.
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, поскольку множество, определяющее его, включает данные типы.
*/