Что происходит при объединении полей с конкретными значениями в TypeScript?
Что проверяют
Различаете ли вы объединение и пересечение типов и понимаете ли, что литеральный тип это не строка, а множество из одного значения. В интерфейсах на этом держится типизация пропсов и вариантов компонента.
Как отвечать
Литеральный тип это конкретное значение в позиции типа: type Mode = "dark" | "light". Объединение через | означает «одно из», пересечение через & означает «и то и другое сразу».
Ключевой момент: пересечение полей с несовместимыми литералами даёт never, а не ошибку в месте объявления.
type A = { kind: "a"; value: string };
type B = { kind: "b"; value: number };
type Both = A & B; // kind: never, value: never
type Either = A | B; // обычно нужно именно этоТип Both объявится молча, а ошибка вылезет там, где такой объект попробуют создать. Для взаимоисключающих вариантов нужен | с общим полем-дискриминантом, тогда работает сужение:
function render(x: Either) {
if (x.kind === "a") x.value.trim();
else x.value.toFixed(2);
}Полезно добавить, что у объединения объектов напрямую доступны только общие поля, что проверка на never в ветке по умолчанию ловит забытый вариант, и что объединение литерала со строкой схлопывается: "a" | string это просто string.
Где спотыкаются
Называют & объединением и ждут от него поведения |. Формулировка «объединить поля» подталкивает к пересечению, но для взаимоисключающих состояний нужно объединение. Пересечение уместно там, где типы дополняют друг друга, например базовые пропсы плюс дополнительные.
Второе: расширение литерала. В const config = { mode: "dark" } поле получает тип string, потому что его можно переприсвоить, и передать такой объект в функцию с параметром "dark" | "light" компилятор не даст. Лечится это через as const или аннотацию переменной, а не приведением as, которое просто затыкает проверку.
Третье: вместо размеченного объединения пишут одну плоскую структуру с необязательными полями. Компилятор перестаёт помогать: состояние с загрузкой и ошибкой одновременно становится допустимым, а все проверки переезжают в рантайм. Дискриминант стоит заводить сразу, даже когда вариантов всего два.