0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>В этом уроке поговорим подробнее про Generic Types. Возьмем для примера массив.</p>
1
<p>В этом уроке поговорим подробнее про Generic Types. Возьмем для примера массив.</p>
2
<p>Массив - это тип-контейнер, который хранит внутри себя значения любого указанного типа. Логика работы массива не зависит от типа данных, хранящихся внутри. Такое определение автоматически говорит о том, что мы имеем дело с обобщенным типом.</p>
2
<p>Массив - это тип-контейнер, который хранит внутри себя значения любого указанного типа. Логика работы массива не зависит от типа данных, хранящихся внутри. Такое определение автоматически говорит о том, что мы имеем дело с обобщенным типом.</p>
3
<p>Чтобы работать с таким типом, нужно конкретизировать внутренний тип в тот момент, когда мы хотим начать работу с данными этого типа:</p>
3
<p>Чтобы работать с таким типом, нужно конкретизировать внутренний тип в тот момент, когда мы хотим начать работу с данными этого типа:</p>
4
<p>Тип, который указывается внутри угловых скобок, называется<strong>параметром типа</strong>. Такое название выбрано неслучайно - указание параметра выглядит как вызов функции. Ниже мы увидим, что такой взгляд на дженерики помогает лучше понять их принцип работы.</p>
4
<p>Тип, который указывается внутри угловых скобок, называется<strong>параметром типа</strong>. Такое название выбрано неслучайно - указание параметра выглядит как вызов функции. Ниже мы увидим, что такой взгляд на дженерики помогает лучше понять их принцип работы.</p>
5
<p>Представим, что мы хотим определить свою коллекцию, которая работает как массив, но с дополнительными возможностями. Такие коллекции часто делают в ORM для работы с данными, загруженными из базы. Опишем сначала конкретную версию этого типа, работающую только с числами и парой стандартных методов:</p>
5
<p>Представим, что мы хотим определить свою коллекцию, которая работает как массив, но с дополнительными возможностями. Такие коллекции часто делают в ORM для работы с данными, загруженными из базы. Опишем сначала конкретную версию этого типа, работающую только с числами и парой стандартных методов:</p>
6
<p>Здесь мы видим, что данные коллекции хранятся в числовом массиве. При этом в типе определено два метода:</p>
6
<p>Здесь мы видим, что данные коллекции хранятся в числовом массиве. При этом в типе определено два метода:</p>
7
<ul><li>Метод forEach передает элементы коллекции в колбек</li>
7
<ul><li>Метод forEach передает элементы коллекции в колбек</li>
8
<li>Метод at возвращает элементы коллекции по указанному индексу</li>
8
<li>Метод at возвращает элементы коллекции по указанному индексу</li>
9
</ul><p>Одна из возможных реализаций этого типа может выглядеть так:</p>
9
</ul><p>Одна из возможных реализаций этого типа может выглядеть так:</p>
10
<p>Теперь попробуем обобщить этот тип, то есть сделать из него дженерик. Для этого нужно сделать две простые вещи:</p>
10
<p>Теперь попробуем обобщить этот тип, то есть сделать из него дженерик. Для этого нужно сделать две простые вещи:</p>
11
<ul><li>для элементов коллекции вместо number написать T или любое другое имя, начинающееся с большой буквы</li>
11
<ul><li>для элементов коллекции вместо number написать T или любое другое имя, начинающееся с большой буквы</li>
12
<li>добавить T как параметр типа к определению</li>
12
<li>добавить T как параметр типа к определению</li>
13
</ul><p>Так это работает на практике:</p>
13
</ul><p>Так это работает на практике:</p>
14
<p>На такое определение типа можно смотреть как на своеобразное определение функции. Для примера попробуем указать конкретный тип - например, MyColl<string>. В таком случае T заменяется на string внутри определения типа. Причем если внутри типа используются другие дженерики, то они вызывают тип дальше. Другими словами, все это работает как вложенные вызовы функций.</p>
14
<p>На такое определение типа можно смотреть как на своеобразное определение функции. Для примера попробуем указать конкретный тип - например, MyColl<string>. В таком случае T заменяется на string внутри определения типа. Причем если внутри типа используются другие дженерики, то они вызывают тип дальше. Другими словами, все это работает как вложенные вызовы функций.</p>
15
<h2>Ограничения дженериков</h2>
15
<h2>Ограничения дженериков</h2>
16
<p>Дженерики могут иметь ограничения. Например, тип, который передается в дженерик, должен реализовывать какой-то интерфейс. Для этого используется ключевое слово extends. Допустим, что наш тип MyColl должен работать только с типами, которые реализуют интерфейс HasId:</p>
16
<p>Дженерики могут иметь ограничения. Например, тип, который передается в дженерик, должен реализовывать какой-то интерфейс. Для этого используется ключевое слово extends. Допустим, что наш тип MyColl должен работать только с типами, которые реализуют интерфейс HasId:</p>
17
<p>Это позволяет нам использовать тип MyColl только с типами, которые реализуют интерфейс HasId. Например, такой код не будет работать:</p>
17
<p>Это позволяет нам использовать тип MyColl только с типами, которые реализуют интерфейс HasId. Например, такой код не будет работать:</p>
18
<p>Сами дженерики встречаются повсеместно в коде библиотек и фреймворков. Например, в React типы компонентов оборачиваются в дженерики, чтобы можно было указать типы пропсов. С помощью дженериков можно создавать более универсальные типы, которые могут работать с разными типами данных, что мы и рассмотрим в следующих уроках.</p>
18
<p>Сами дженерики встречаются повсеместно в коде библиотек и фреймворков. Например, в React типы компонентов оборачиваются в дженерики, чтобы можно было указать типы пропсов. С помощью дженериков можно создавать более универсальные типы, которые могут работать с разными типами данных, что мы и рассмотрим в следующих уроках.</p>