Тип в TypeScript можно определять как набор значений. Например, тип number можно представить как набор всех чисел. Из этого следует, что 1,0 и 68 принадлежат этому набору, а "bytefer" не принадлежит ему, поскольку относится к типу string.

Аналогичным образом объектные типы можно воспринимать как наборы объектов. Например, тип Point в следующем фрагменте кода представляет собой набор объектов со свойствами x и y, причем оба типа значений свойств являются числовыми. Тип Named представляет собой набор объектов, содержащих свойство name, а тип значения свойства — строковый.
interface Point {
x: number;
y: number;
}
interface Named {
name: string;
}
Согласно теории множеств, если A и B — множества, то множество, состоящее из всех элементов, принадлежащих множеству A и множеству B, является пересечением множества A и множества B.

При пересечении типов Point и Named создается новый тип. Объекты, содержащиеся в нем, принадлежат как Point, так и Named.
В TypeScript для реализации операции пересечения нескольких типов предусмотрен оператор &, а полученный новый тип называется типом пересечения.
Оператор & удовлетворяет следующим правилам.
- Идентичность:
A & AэквивалентноA. - Коммутативность (независимость от порядка выполнения):
A & BэквивалентноB & A(за исключением сигнатур вызова и конструктора, как будет отмечено ниже). - Ассоциативность:
(A & B) & CэквивалентноA & (B & C). - Сокращение супертипов:
A & BэквивалентноA, еслиBявляется супертипомA.

В приведенном выше коде типы any и never являются специальными. Тип any, пересекающийся с типом any, приводит к типу any (что не происходит при пересечении any с never).
Теперь, когда мы познакомились с оператором &, посмотрим, какой тип получится при пересечения Point и Named.

Созданный тип NamedPoint содержит свойства x, y и name. Что же происходит, когда пересекаются несколько типов объектов, содержащих одинаковые атрибуты, но чьи свойства не являются однотипными?
interface X {
c: string;
d: string;
}
interface Y {
c: number;
e: string
}
type XY = X & Y;
type YX = Y & X;
В приведенном выше коде interface X и interface Y содержат одно и то же свойство c, но их типы не совпадают. Может ли в этом случае тип атрибута c в типе XY или YX быть строковым или числовым? Проверим это:
Почему после пересечения interface X и interface Y тип свойства c становится never? Потому что тип свойства c после этой операции — string & number, то есть он может быть либо строковым, либо числовым. Очевидно, что такого типа не существует, поэтому тип свойства c после операции — never.
В предыдущем примере получилось так, что типы свойства c в interface X и interface Y являются примитивными типами данных. Что же произойдет, если разные типы объектов содержат одно и то же свойство, а тип свойства не является примитивным? Рассмотрим конкретный пример:

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

Как видно из приведенного выше кода, только оператор вызова функции f(1, "bytefer") выдает ошибку. Сообщение об ошибке выглядит следующим образом.
No overload matches this call.
Overload 1 of 2, '(a: string, b: string): void', gave the following error.
Argument of type 'number' is not assignable to parameter of type 'string'.
Overload 2 of 2, '(a: number, b: number): void', gave the following error.
Argument of type 'string' is not assignable to parameter of type 'number'.ts(2769)
Ни одна перегрузка не соответствует этому вызову.
Перегрузка 1 из 2,
(a: string, b: string): void, выдала следующую ошибку.
Аргумент типа
numberне может быть присвоен параметру типаstring.
Перегрузка 2 из 2,
(a: число, b: число): void, привела к следующей ошибке.
Аргумент типа
stringне может быть присвоен параметру типа‘number’.ts(2769).
Исходя из приведенного выше сообщения об ошибке, становится ясно: компилятор TypeScript будет использовать перегрузку функции для достижения операций пересечения различных типов функций. Чтобы решить эту проблему, определим новый тип функции F3 следующим образом.

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

Читайте также:
- Краткий обзор нововведений TypeScript 4.1
- Знакомство с SurrealDB с помощью Express.js, Node.js и TypeScript
- Как создать простую функцию AWS Lambda с помощью TypeScript
Читайте нас в Telegram, VK и Дзен
Перевод статьи Bytefer: How to Use TypeScript Intersection Types Like a Pro




