Автор оригинала: Giorgio Delgado.
Мы все были там раньше. Мы пишем функцию, которая должна иметь дело с некоторым краем, и мы используем бросить
Ключевое слово для решения этой ситуации:
type ResponseData = { statusCode: number responseBody?: ResponseBody } const makeHttpRequest = async (url: string): Promise=> { if (!isUrl(url)) { throw new Error( 'Invalid string passed into `makeHttpRequest`. Expected a valid URL.' ) } // ... // other business logic here // ... return { ... } // ResponseData }
Теперь представьте через месяц спустя, вы работаете над своим проектом, когда вы или коллега забудьте обернуть makehttprequest
внутри попробуйте/поймать
блокировать.
Здесь происходят две вещи:
Компилятор больше не в состоянии сказать, ваш код безопасен от ошибок выполнения. Другими словами, используя
бросить
не типовойafe. И так же опасно, каклюбой
Отказ Они оба разбавляют преимущества использования TypeScript в первую очередь.Потому что ни компилятор, ни типы не говорят вам, что
makehttprequest
Может выйти из строя (читать: бросить), вы в конечном итоге получите ошибку выполнения. Это пустая трата времени, денег и счастья для всех. Люди начинают задавать, почему они используют TypectScript, если компилятор не помогает им с чем-то таким базовым, как добавлениепопробуйте/поймать
блокировать.
Так что вопрос в том,
Как мы кодируем сжатие в типографию?
Во-первых, давайте начнем с признания этого бросить
не типовойafe. Мы должны использовать другой подход для того, чтобы получить компилятор Tymdercript на нашей стороне.
Что если у нас было Тип
или Интерфейс
который представлял результат вычисления, который может потерпеть неудачу?
Наш тип будет представлять два простых результата:
- Кейс успеха: который бы вернул/содержать содержать «значение успеха» (I.E.
Reffectedata
в случаеmakehttprequest
) - Сбой случая: который бы вернул/содержит полезную информацию о Почему произошла неудача
Давайте позвоним нашему типу, что-то интуитивно понятное, как Результат
Отказ Давайте назовем наш вариант успеха Хорошо
и наш отказ вариант Err
Отказ
Таким образом, если бы мы были формализовать наш тип в код, он будет выглядеть что-то подобное:
type Result= Ok // contains a success value of type T | Err // contains a failure value of type E
Возвращаясь к нашему makehttprequest
Функция, мы хотели бы кодировать потенциал для неудачи в типографию.
Таким образом makehttprequest
будет иметь следующую подпись:
makeHttpRequest(url: string): Promise>
И определение функции будет выглядеть что-то подобное:
// utility functions to build Ok and Err instances const ok =(value: T): Result => new Ok(value) const err = (error: E): Result => new Err(error) const makeHttpRequest = async (url: string): Promise > => { if (!isUrl(url)) { return err(new Error( 'Invalid string passed into `makeHttpRequest`. Expected a valid URL.' )) } // ... // other business logic here // ... return ok({ ... }) // Ok(ResponseData) }
Конечно ERR (новая ошибка («...»))
кажется немного утомительным. Но вот некоторые вещи, которые вы должны знать:
Аргумент
Err
Функция должна быть типаЕ
или вы получите ошибку компиляции (несоответствие типа) между типом внутриErr
и возвратный типmakehttprequest
(где типE
тип представлен какошибка
экземпляр).- Свидетельство, я просто выбрал
Ошибка
. Как тип дляЕ
Ради простоты … ЗначениеЕ
может быть все, что вы хотите! Больше на это немного!
- Свидетельство, я просто выбрал
Пользователь
makehttprequest
Можно использовать эту функцию, не опасаясь, что это может произвольно бросить. Нет больше ошибок времени выполнения 🚀Автор
makehttprequest
Функция также не должна беспокоиться о написании и обновлении документации каждый раз, когда появляется новый крайний корпус, который приведет к ошибке функции. Все поведение функции кодируется в типе возврата. Соответственно, тип служит документацией сейчас: «makehttprequest
– асинхронная функция, которая может либо добиться успеха сrespurededata
или сбой с помощью |
… “Но подожди, как мне получить значение T
Value или E
Значение, которое завернуто внутри Результат
?”
Отличный вопрос. Позвольте мне показать вам, как. Мы собираемся использовать пакет, который я сделал [indly] по имени Неворотный
Отказ
> NPM Установка неворота
import { ok, err, Result } from 'neverthrow' // we'll keep this simple type ResponseBody = {} interface ResponseData { statusCode: number responseBody?: ResponseBody } const makeHttpRequest = async ( url: string ): Promise> => { if (!isUrl(url)) { return err(new Error( 'Invalid string passed into `makeHttpRequest`. Expected a valid URL.' )) } // ... // other business logic here // ... return ok({ ... }) // Ok(ResponseData) }
Поэтому в настоящее время мы находимся в том же месте, что мы были на последнем фрагменте кода, кроме этого времени, мы используем Неворотный
упаковка.
Если бы вы прочитали через Неворотный
Документация Вы увидите, что Результат
имеет .map
Метод, который принимает T
Значение внутри Результат
и преобразует его во все, что вы хотите.
Вот пример:
import { makeHttpRequest } from './http-api.ts' const run = async () => { // unwrap the Promise // at this point // we have a Resultconst result = await makeHttpRequest('https://jsonplaceholder.typicode.com/todos/1') result.map(responseData => { console.log(responseData) }) } run()
Но подождите, что если переменная результата содержит Е
значение? Другими словами, это Err
вместо Хорошо
Отказ
Ну, опять же, документы для Неворотный
Покажите, как справиться с этой ситуацией … просто используйте Maperr
!
import { makeHttpRequest } from './http-api.ts' const run = async () => { // unwrap the Promise // at this point // we have a Resultconst result = await makeHttpRequest('https://jsonplaceholder.typicode.com/todos/1') result.mapErr(errorInstance => { console.log(errorInstance) }) } run()
Самая красивая вещь о Результат
с такой Они церемымыми ! Вот вышеуказанный код в более реалистичном примере:
import { makeHttpRequest } from './http-api.ts' const run = async () => { // unwrap the Promise // at this point // we have a Resultconst result = await makeHttpRequest('https://jsonplaceholder.typicode.com/todos/1') result .map(responseData => { // do something with the success value }) .mapErr(errorInstance => { // do something with the failure value }) } run()
Есть намного больше, вы можете сделать с Результат
Тип (проверьте Документы ), но карта
ING – самая важная часть API.
Сделать ваши виды более интуитивными
Если вы начнете использовать Результат
Много в ваших возвращаемых типах вы можете заметить две вещи:
Значение
Результат
S не очень ясно- Пример:
Результат
запроса баз данных может быть что-то вродеОбещание <результат
пока> Результат
сетевого звонка может быть что-то вродеОбещание <результат
Отказ>
- Пример:
Типы действительно длинные и многословные. Например, выше у нас был
Обещание <результат
… Это несколько запугивающим типом!>
Чтобы решить обе проблемы, вы можете использовать Тип псевдонимов !
Вот пример. Вместо того, чтобы иметь функцию, вернуть общий Результат
с ДЕРЕРРОР
Как Е
Тип, почему бы не псевдоним этот тип к чему-то гораздому интуитивному?
type DbResult= Result
Теперь ваша функция может просто вернуть Обещание
Отказ Это намного более сжато!
Кроме того, ваш тип сейчас кодирует значение. Вышеуказанный тип говорит, что происходит что-то Async, которое может потерпеть неудачу, и я знаю, что он имеет дело с базой данных. Аккуратный!
Вот реальный пример от одного из моих проектов:
handler: (req: Request, res: SessionManager) => DecodeResult>>
Итак, обработчик
это функция, которая делает несколько вещей:
- Он делает некоторую декодирование/десериализацию входящих данных
- Затем это делает что-то async, которое генерирует
RouterSult
С некоторыми данными типаТ
Я точно знаю, что произойдет, только чтение типов. И прекрасная вещь в том, что я никогда не получу ошибки времени выполнения, потому что ни один из моих кодов не бросает (и все 3-й партийные Libs, которые я зависим от, были обернуты к возвращению результат
).
Резюме
Избегайте использования
бросить
если ты можешь.- Пользователи ваших API не требуются для
поймать
(компилятор не применяет его). Это означает, что вы в конечном итоге попадут в ошибку выполнения … это просто вопрос времени - Использование
бросить
заставляет вас поддерживать документацию, которая в конечном итоге станет устаревшей - Если вам удастся поддерживать документацию, многие люди не будут полностью начать читать через документацию, и не поймет, что ваши функции бросают в определенные сценарии
- Пользователи ваших API не требуются для
Закомите потенциал для сбоя в ваши типы, используя
Результат
с- Типы самодоступны, и они не могут стать «устаревшей»
- Пользователи предоставляются дружелюбные API, которые позволяют им справиться с неудачными значениями безопасным способом (используя
Map
иMaperr
)
Там есть полностью проверенная и тип, проверенная типа NPM для этого называется
Неворотный
… Попробуйте!