diff --git a/_overviews/scala3-book/types-intersection.md b/_overviews/scala3-book/types-intersection.md
index 8c01dd9b33..66a6f3ec0d 100644
--- a/_overviews/scala3-book/types-intersection.md
+++ b/_overviews/scala3-book/types-intersection.md
@@ -2,7 +2,7 @@
title: Intersection Types
type: section
description: This section introduces and demonstrates intersection types in Scala 3.
-languages: [zh-cn]
+languages: [ru, zh-cn]
num: 50
previous-page: types-generics
next-page: types-union
diff --git a/_overviews/scala3-book/types-union.md b/_overviews/scala3-book/types-union.md
index 271c9a0148..50c5919980 100644
--- a/_overviews/scala3-book/types-union.md
+++ b/_overviews/scala3-book/types-union.md
@@ -2,7 +2,7 @@
title: Union Types
type: section
description: This section introduces and demonstrates union types in Scala 3.
-languages: [zh-cn]
+languages: [ru, zh-cn]
num: 51
previous-page: types-intersection
next-page: types-adts-gadts
diff --git a/_ru/scala3/book/types-generics.md b/_ru/scala3/book/types-generics.md
index 799a85e3d5..653d6cacbd 100644
--- a/_ru/scala3/book/types-generics.md
+++ b/_ru/scala3/book/types-generics.md
@@ -9,7 +9,7 @@ description: В этом разделе представлены парамет
language: ru
num: 49
previous-page: types-inferred
-next-page:
+next-page: types-intersection
---
Универсальные (_generic_) классы (или trait-ы) принимают тип в качестве _параметра_ в квадратных скобках `[...]`.
diff --git a/_ru/scala3/book/types-intersection.md b/_ru/scala3/book/types-intersection.md
new file mode 100644
index 0000000000..6fa78e33ab
--- /dev/null
+++ b/_ru/scala3/book/types-intersection.md
@@ -0,0 +1,76 @@
+---
+layout: multipage-overview
+title: Пересечение типов
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: section
+description: В этом разделе представлены пересечение типов в Scala 3.
+language: ru
+num: 50
+previous-page: types-generics
+next-page: types-union
+---
+
+Только в Scala 3
+
+Используемый для типов оператор `&` создает так называемый _тип пересечения_ (_intersection type_).
+Тип `A & B` представляет собой значения, которые **одновременно** относятся как к типу `A`, так и к типу `B`.
+Например, в следующем примере используется тип пересечения `Resettable & Growable[String]`:
+
+{% tabs intersection-reset-grow %}
+
+{% tab 'Только в Scala 3' %}
+
+```scala
+trait Resettable:
+ def reset(): Unit
+
+trait Growable[A]:
+ def add(a: A): Unit
+
+def f(x: Resettable & Growable[String]): Unit =
+ x.reset()
+ x.add("first")
+```
+
+{% endtab %}
+
+{% endtabs %}
+
+В методе `f` в этом примере параметр `x` должен быть _одновременно_ как `Resettable`, так и `Growable[String]`.
+
+Все _члены_ типа пересечения `A & B` являются типом `A` и типом `B`.
+Следовательно, как показано, для `Resettable & Growable[String]` доступны методы `reset` и `add`.
+
+Пересечение типов может быть полезно для _структурного_ описания требований.
+В примере выше для `f` мы прямо заявляем, что нас устраивает любое значение для `x`,
+если оно является подтипом как `Resettable`, так и `Growable`.
+**Нет** необходимости создавать _номинальный_ вспомогательный trait, подобный следующему:
+
+{% tabs normal-trait class=tabs-scala-version %}
+{% tab 'Scala 2' %}
+
+```scala
+trait Both[A] extends Resettable with Growable[A]
+def f(x: Both[String]): Unit
+```
+
+{% endtab %}
+
+{% tab 'Scala 3' %}
+
+```scala
+trait Both[A] extends Resettable, Growable[A]
+def f(x: Both[String]): Unit
+```
+
+{% endtab %}
+{% endtabs %}
+
+Существует важное различие между двумя вариантами определения `f`:
+в то время как оба позволяют вызывать `f` с экземплярами `Both`,
+только первый позволяет передавать экземпляры,
+которые являются подтипами `Resettable` и `Growable[String]`, _но не_ `Both[String]`.
+
+> Обратите внимание, что `&` _коммутативно_: `A & B` имеет тот же тип, что и `B & A`.
diff --git a/_ru/scala3/book/types-union.md b/_ru/scala3/book/types-union.md
new file mode 100644
index 0000000000..35a0f440f6
--- /dev/null
+++ b/_ru/scala3/book/types-union.md
@@ -0,0 +1,111 @@
+---
+layout: multipage-overview
+title: Объединение типов
+scala3: true
+partof: scala3-book
+overview-name: "Scala 3 — Book"
+type: section
+description: В этом разделе представлены объединение типов в Scala 3.
+language: ru
+num: 51
+previous-page: types-intersection
+next-page:
+---
+
+Только в Scala 3
+
+Используемый для типов `|` оператор создает так называемый _тип объединения_ (_union type_).
+Тип `А | B` представляет значения, которые относятся **либо** к типу `A`, **либо** к типу `B`.
+
+В следующем примере метод `help` принимает параметр с именем `id` типа объединения `Username | Password`,
+который может быть либо `Username`, либо `Password`:
+
+```scala
+case class Username(name: String)
+case class Password(hash: Hash)
+
+def help(id: Username | Password) =
+ val user = id match
+ case Username(name) => lookupName(name)
+ case Password(hash) => lookupPassword(hash)
+ // дальнейший код ...
+```
+
+Мы реализуем метод `help`, разделяя две альтернативы с использованием сопоставления с образцом.
+
+Этот код является гибким и типобезопасным решением.
+Если попытаться передать тип, отличный от `Username` или `Password`, компилятор пометит это как ошибку:
+
+```scala
+help("hi") // error: Found: ("hi" : String)
+ // Required: Username | Password
+```
+
+Ошибка также будет получена, если попытаться добавить `case` в выражение `match`,
+которое не соответствует типам `Username` или `Password`:
+
+```scala
+case 1.0 => ??? // Ошибка: это строка не компилируется
+```
+
+### Альтернатива объединенным типам
+
+Как показано, объединенные типы могут использоваться для представления вариантов нескольких разных типов,
+не требуя, чтобы эти типы были частью специально созданной иерархии классов.
+
+#### Предварительное планирование иерархии классов
+
+Другие языки требуют предварительного планирования иерархии классов, как показано в следующем примере:
+
+```scala
+trait UsernameOrPassword
+case class Username(name: String) extends UsernameOrPassword
+case class Password(hash: Hash) extends UsernameOrPassword
+def help(id: UsernameOrPassword) = ...
+```
+
+Предварительное планирование не очень хорошо масштабируется,
+поскольку, например, требования пользователей API могут быть непредсказуемыми.
+Кроме того, загромождение иерархии типов маркерами типа `UsernameOrPassword` затрудняет чтение кода.
+
+#### Теговые объединения
+
+Другой альтернативой является задание отдельного типа перечисления, например:
+
+```scala
+enum UsernameOrPassword:
+ case IsUsername(u: Username)
+ case IsPassword(p: Password)
+```
+
+Перечисление `UsernameOrPassword` представляет собой _помеченное_ (_tagged_) объединение `Username` и `Password`.
+Однако этот способ моделирования объединения требует _явной упаковки и распаковки_,
+и, например, `Username` **не** является подтипом `UsernameOrPassword`.
+
+### Вывод типов объединения
+
+Компилятор присваивает типу объединения выражение, _только если_ такой тип явно задан.
+Например, рассмотрим такие значения:
+
+```scala
+val name = Username("Eve") // name: Username = Username(Eve)
+val password = Password(123) // password: Password = Password(123)
+```
+
+В этом REPL примере показано,
+как можно использовать тип объединения при привязке переменной к результату выражения `if`/`else`:
+
+```
+scala> val a = if true then name else password
+val a: Object = Username(Eve)
+
+scala> val b: Password | Username = if true then name else password
+val b: Password | Username = Username(Eve)
+```
+
+Типом `a` является `Object`, который является супертипом `Username` и `Password`,
+но не _наименьшим_ супертипом, `Password | Username`.
+Если необходим наименьший супертип, его нужно указать явно, как это делается для `b`.
+
+> Типы объединения являются двойственными типам пересечения.
+> И как `&` с типами пересечения, `|` также коммутативен: `A | B` того же типа, что и `B | А`.