После того как я некоторое время писал TypeScript (TS), я понял одну вещь: «Мы пишем TS, чтобы продолжать писать JS». Раньше я сыпал аннотациями типов ради типов, но это не работает. Что я должен делать, так это писать TS в одном месте, и поместить структуру, чтобы я мог вывести тип оттуда, где он мне нужен, без необходимости управлять большим количеством типов.
Чтобы увидеть разницу, вот как я бы написал обработчик API до и после применения новой структуры:
Этот пост — моя попытка извлечь то, что я хотел бы знать, когда начинал. Знания о TS применимы везде, где вы используете TS, но я буду использовать Next.js для представления идеи.
Прежде чем продолжить, шаг 1 — 2 довольно прост, если вы знакомы с TS — так что не стесняйтесь пропустить. Процесс набора текста начинается с шага 3.
1. Настройка сцены
Давайте подготовим сцену, создав репозиторий Next.js, который использует TS из коробки:
npx create-next-app@latest --ts
После этого у вас будут эти файлы:
На момент написания этой статьи я использую Next.js 12.
Чтобы убедиться, что Next.js готов, запустите yarn dev
и curl
конечную точку api/hello
, чтобы увидеть ее ответ. Когда вы остановите терминальную сессию yarn dev
(используйте ctrl+c
), curl
больше не должен работать.
Теперь установим другие пакеты (мы объясним их применение по ходу дела):
yarn add zod http-status-code @sentry/nextjs
2. Использование абсолютного импорта
Откройте файл pages/api/hello.ts
в vscode и добавьте этот оператор импорта, вы увидите красную загогулину:
TS пытался найти пакет @backend
в node_modules, но не смог найти, отсюда и ошибка. Нам не нужно устанавливать пакет, потому что это будет папка, на которую мы будем ссылаться с помощью абсолютного импорта. Давайте установим его:
- Откройте ваш
tsconfig.json
и добавьте эти строки нижеcompilerOptions
:
"baseUrl": ".",
"paths": {
"@api/*": [
"pages/api/*"
],
"@backend": [
"backend"
],
"@backend/*": [
"backend/*"
]
},
Далее создадим новую папку backend
и создадим в ней файл index.ts
:
Откройте файл pages/api/hello.ts
снова, и красная тильда исчезла!
Основываясь на новых добавленных baseUrl
и paths
в нашем tsconfig.json
, TS знает, какую папку найти, когда видит "@backend"
. Мы называем эту настройку «абсолютным импортом». Использование абсолютного импорта проще по сравнению с относительным импортом, где нам приходится использовать ../
или ../../
для доступа к файлам в родительских папках.
1) Поскольку мы добавили
"baseUrl":"."
и папкаbackend
фактически находится в корневом каталоге проекта, мы можем сделатьimport {} "backend"
и это все равно будет работать. Мы будем придерживаться"@backend"
, потому что (на мой взгляд) префикс@
помогает намекнуть, что это абсолютный импорт.2) Мы также добавили абсолютный путь для
"@api/*"
, к которому мы перейдем позже.
3. Добавляем файлы в папку backend
Откройте этот Github gist и скопируйте содержимое в соответствующий файл в папке backend
:
Ваша папка backend должна выглядеть следующим образом:
Когда все будет готово, запустим проверку типов, чтобы убедиться в отсутствии ошибок:
yarn tsc --noEmit
Мы используем
--noEmit
, чтобы указать TS запустить проверку типов без фактической компиляции файлов в JS.
4. Давайте посмотрим типы!
Откройте файл pages/api/hello.ts
и обратите внимание, что Next.js добавил тип Data
для ответа JSON. Если вы передадите неправильную форму для параметра, TS будет жаловаться:
Попробуйте сохранить файл при наличии красной волнистой линии и запустите проверку типа (yarn tsc --noEmit
):
Вы видите, что проверка типа не прошла, потому что возникла ошибка. Это один из способов использования TS для предотвращения попадания случайных ошибок в продакшн. Например, мы можем запускать проверку типов автоматически (например, с помощью Github Action) для каждого коммита и предотвращать слияние коммита в main
, если проверка не прошла.
Теперь мы знаем, что Next.js добавил тип для данных ответа. Но что, если мы хотим задать тип и для запроса? Откройте этот gist и скопируйте содержимое в pages/api/greeting.ts
:
Вот как мы читаем приведенные выше коды:
-
Строки 13-16
: определяем функциюhandler
, которая является мясом нашего кода API. Мы используемApiHandler
, дженерик, который мы определили вbackend/types.ts:25
, для добавления типов к нашим объектам запроса и ответа на основе типаschema
иresponse
. -
Строка 18
: передайтеhandler
нашей функцииhandle
, которая автоматически проверит запрос на соответствие схеме. Это гарантирует, что все свойства, определенные вschema
, будут доступны вhandler
. Например, он отменит запрос и вернет ответ об ошибке, если пользователь не предоставитname
в параметрах запроса. Таким образом, нашемуhandler
не придется заниматься ручной проверкой (например, проверять, не является лиname
пустым).
Вот так! Теперь у нас есть структура для типизации нашего API. Мне нравится, как она начинается с декларативного стиля (объявление формы схемы и ответа) и продолжается императивным стилем (обработчик).
Когда у нас будет несколько файлов с похожей структурой, их будет легко просмотреть, потому что есть шаблон: форма схемы, форма ответа, обработчик. Обработчик тоже довольно тонкий, потому что ему не нужно заботиться о проверке данных.
В следующей части мы посмотрим, как мы повторно используем response
, чтобы добавить типизацию в наш React-компонент. Мы также добавим структуру для тестирования бэкенда и фронтенда с помощью jest
. Вот краткое описание:
Оставайтесь с нами!