Некоторые шаблоны React сложнее реализовать в TypeScript, чем другие, и требуют знания правильного подхода и использования правильных функций TypeScript. Компонент высшего порядка - один из таких паттернов.

В этом посте мы рассмотрим создание компонента более высокого порядка с нуля и динамическое вычисление его требований к опорам с использованием универсальных и служебных типов.

Что такое компонент более высокого порядка?

Компонент высшего порядка (HOC) - это функция, которая принимает другой компонент и вводит в него дополнительные свойства. Думайте об этом как об обертке вашего компонента дополнительным слоем, который придает ему больше функциональности.

Целью HOC часто является повторное использование общей логики между компонентами и отдельных логических и презентационных уровней. С появлением крючков HOC по большей части отошли на второй план. Хотя вы видите их реже, HOC по-прежнему актуальны и имеют определенные преимущества перед хуками, например лучшую совместимость с компонентами на основе классов.

Хорошо известным примером HOC является функция withRouter из библиотеки react-router.

Настройка HOC

Давайте начнем с создания строительных лесов для HOC. Мы создадим простой HOC, который добавит функции таймера к нашим компонентам. В нашей первой итерации не будет строгой типизации, но мы будем совершенствовать ее по мере продвижения.

Вот как выглядит первая версия нашего компонента высшего порядка:

На этом этапе наш HOC не очень безопасен для типов, поскольку мы устанавливаем any для типа целевого компонента. Нет проверки типа, чтобы убедиться, что передаваемый компонент принимает пропсы, которые мы пытаемся внедрить.

Теперь давайте начнем ужесточать проверку типов.

Добавление дженериков

Давайте сделаем так, чтобы наш HOC принимал только компоненты React и ожидал тех же свойств, что и целевой компонент. Мы можем использовать дженерики TypeScript, чтобы наложить эти ограничения:

ComponentType - это специальный тип, который React предоставляет для работы с компонентами в TypeScript.

Также обратите внимание на то, что мы используем дженерики. Мы использовали тип T в нескольких местах:

  • Мы устанавливаем для нашего параметра тип ComponentType<T>. Теперь в рамках этой функции T обозначает тип свойств целевого компонента.
  • Мы также устанавливаем тип hocProps на T, чтобы наш компонент HOC получал те же свойства, что и цель.

Благодаря дженерикам TypeScript может динамически вычислять все свойства, которые принимает наш исходный компонент, и применять такое же ограничение для HOC. Вы можете узнать больше о дженериках из Документации по TypeScript.

Использование типов утилит

Проверка типов в нашем HOC пока идет хорошо. Вот как мы будем его использовать:

Но есть еще одна проблема, которую нам нужно исправить. Когда вы запускаете наш HOC, вы получаете сообщение об ошибке TypeScript:

Ранее я заявлял, что HOC должен ожидать те же самые свойства T, что и целевой компонент, но это не полностью верно. Наш HOC не должен ожидать реквизитов counter, startTimer и endTimer, потому что задача компонента более высокого порядка - внедрить эти реквизиты в первую очередь.

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

Мы внесли еще два изменения в наш код:

  • Теперь наш HOC принимает реквизиты типа hocProps: Omit<T, "count" | "startTimer" | "endTimer">. Используя Omit, мы создали новый тип, который ожидает все те же свойства T, за исключением count, startTimer и endTimer.
  • Поскольку Omit создает новый тип, нам пришлось использовать обходной путь {...(hocProps as T)}, чтобы сообщить TypeScript, что мы ожидаем, что hocProps будет почти идентичным T, за исключением пропущенных нами свойств.

Теперь наш компонент более высокого порядка готов к использованию!

Заключение

В этом посте мы рассмотрели правильный способ написания компонентов высшего порядка на TypeScript. Мы сделали наш HOC гибким и безопасным с использованием универсальных и служебных типов TypeScript. Прочтите мой пост, чтобы узнать больше о других типах утилит TypeScript, полезных для React.

Вот ссылка на песочницу кода для этого руководства. Спасибо за чтение!

Первоначально опубликовано на https://isamatov.com 8 августа 2021 г.