HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>Статическая типизация защищает код от большого класса ошибок, связанных с неправильным использованием типов. Но у всего есть своя цена. В некоторых ситуациях достаточно добавить описание типов, в некоторых приходится вводить новые и не всегда простые понятия, например, дженерики. В этом уроке мы начнем знакомиться с ними.</p>
1 <p>Статическая типизация защищает код от большого класса ошибок, связанных с неправильным использованием типов. Но у всего есть своя цена. В некоторых ситуациях достаточно добавить описание типов, в некоторых приходится вводить новые и не всегда простые понятия, например, дженерики. В этом уроке мы начнем знакомиться с ними.</p>
2 <p>Представим функцию слияния двух массивов. На JavaScript этот код записывается достаточно просто:</p>
2 <p>Представим функцию слияния двух массивов. На JavaScript этот код записывается достаточно просто:</p>
3 <p>Удобство динамической типизации тут проявляется в том, что эта функция автоматически работает для любых массивов, что бы в них ни хранилось.</p>
3 <p>Удобство динамической типизации тут проявляется в том, что эта функция автоматически работает для любых массивов, что бы в них ни хранилось.</p>
4 <p>В статически типизированных языках такой трюк не пройдет. Придется указывать конкретный тип:</p>
4 <p>В статически типизированных языках такой трюк не пройдет. Придется указывать конкретный тип:</p>
5 <p>Если нужно сливать массивы, состоящие из строк, то придется использовать перегрузку функций. Но внутри возникнет проблема с возвращаемым типом, который будет разный в зависимости от входных параметров:</p>
5 <p>Если нужно сливать массивы, состоящие из строк, то придется использовать перегрузку функций. Но внутри возникнет проблема с возвращаемым типом, который будет разный в зависимости от входных параметров:</p>
6 <p>В языках с настоящей перегрузкой функций проблема будет заключаться в том, что появится много функций, у которых одинаковое тело. То есть по сути дублирование логики для всех возможных входных типов. Эта ситуация настолько распространенная и непростая, что для нее создана целая подсистема в системе типов. Она называется дженериками.</p>
6 <p>В языках с настоящей перегрузкой функций проблема будет заключаться в том, что появится много функций, у которых одинаковое тело. То есть по сути дублирование логики для всех возможных входных типов. Эта ситуация настолько распространенная и непростая, что для нее создана целая подсистема в системе типов. Она называется дженериками.</p>
7 <p><strong>Дженерики</strong>в применении к функциям - это механизм, позволяющий создать такие функции, которые имеют одинаковую логику обработки для разных типов данных. Иногда такие функции называют обобщенными функциями.</p>
7 <p><strong>Дженерики</strong>в применении к функциям - это механизм, позволяющий создать такие функции, которые имеют одинаковую логику обработки для разных типов данных. Иногда такие функции называют обобщенными функциями.</p>
8 <p>Ниже пример реализации функции merge() в обобщенном виде:</p>
8 <p>Ниже пример реализации функции merge() в обобщенном виде:</p>
9 <p>Здесь мы видим новый синтаксис, к которому нужно привыкнуть. Если не вдаваться в детали, запись в &lt;T&gt; после имени функции говорит о том, что перед нами дженерик, который параметризуется типом T. Тип T означает, что мы могли бы использовать любую другую заглавную букву, например, X. Чаще всего мы будем видеть это обозначение, потому что это общепринятая практика.</p>
9 <p>Здесь мы видим новый синтаксис, к которому нужно привыкнуть. Если не вдаваться в детали, запись в &lt;T&gt; после имени функции говорит о том, что перед нами дженерик, который параметризуется типом T. Тип T означает, что мы могли бы использовать любую другую заглавную букву, например, X. Чаще всего мы будем видеть это обозначение, потому что это общепринятая практика.</p>
10 <p>Что конкретно скрывается под типом с точки зрения кода дженерика - не важно. Это может быть объект, число, строка или булево значение. В вызовах примера выше это число для первого вызова и строка для второго. Так же можно было бы сделать вызов с булевыми значениями:</p>
10 <p>Что конкретно скрывается под типом с точки зрения кода дженерика - не важно. Это может быть объект, число, строка или булево значение. В вызовах примера выше это число для первого вызова и строка для второго. Так же можно было бы сделать вызов с булевыми значениями:</p>
11 <p>Дальше уже внутри функции мы видим, что логика работы одинакова для всех типов и не зависит от типа. Мы просто перекладываем элементы массивов в другой массив. В этом месте код выглядит уже привычно.</p>
11 <p>Дальше уже внутри функции мы видим, что логика работы одинакова для всех типов и не зависит от типа. Мы просто перекладываем элементы массивов в другой массив. В этом месте код выглядит уже привычно.</p>
12 <p>Осталось разобраться с параметрами и возвращаемым значением.</p>
12 <p>Осталось разобраться с параметрами и возвращаемым значением.</p>
13 <p>Запись Array&lt;T&gt; описывает обобщенный массив - тоже дженерик, но уже для типа. На месте этого параметра может оказаться любой массив, например, number[] или boolean[]. Соответственно, в коде функции мы говорим о том, что ожидаем на вход два массива одного типа, и этот же тип является выходным.</p>
13 <p>Запись Array&lt;T&gt; описывает обобщенный массив - тоже дженерик, но уже для типа. На месте этого параметра может оказаться любой массив, например, number[] или boolean[]. Соответственно, в коде функции мы говорим о том, что ожидаем на вход два массива одного типа, и этот же тип является выходным.</p>
14 <p>Имя параметра типа T имеет тут важную роль. Если бы мы использовали другую букву, то ее нужно было бы поменять для всех частей внутри:</p>
14 <p>Имя параметра типа T имеет тут важную роль. Если бы мы использовали другую букву, то ее нужно было бы поменять для всех частей внутри:</p>
15 <p>Так TypeScript понимает, что типы входных массивов и результирующего совпадают. То есть не получится вызвать эту функцию, передав туда одновременно массив из чисел и строк.</p>
15 <p>Так TypeScript понимает, что типы входных массивов и результирующего совпадают. То есть не получится вызвать эту функцию, передав туда одновременно массив из чисел и строк.</p>
16 <p>Но типы могут и не совпадать. Ниже пример дженерика, который возвращает первый элемент любого массива и null, если он пустой:</p>
16 <p>Но типы могут и не совпадать. Ниже пример дженерика, который возвращает первый элемент любого массива и null, если он пустой:</p>
17 <p>Дженерики - это большая тема, которая раскрывается в следующих уроках. Сейчас мы только познакомились с общей концепцией и постепенно начинаем ее использовать.</p>
17 <p>Дженерики - это большая тема, которая раскрывается в следующих уроках. Сейчас мы только познакомились с общей концепцией и постепенно начинаем ее использовать.</p>