
Кодировать сбой в вашу программу.
Этот пакет содержит тип Result , который представляет либо успех ( Ok ), либо сбой ( Err ).
Для асинхронных задач neverthrow предлагает класс ResultAsync , который охватывает Promise<Result<T, E>> и дает вам такой же уровень выразительности и управления, что и обычный Result<T, E> .
ResultAsync является thenable означающее, что он ведет себя точно так же, как нативное Promise<Result> ... за исключением того, что у вас есть доступ к тем же методам, которые дают Result , не await или .then обещание! Проверьте вики для примеров и лучших практик.
Нужно увидеть реальные примеры того, как использовать этот пакет для обработки ошибок? Смотрите это репо: https://github.com/parlez-vous/server
eslint-plugin-neverthrowResult )okerrResult.isOk (метод)Result.isErr (метод)Result.map (метод)Result.mapErr (метод)Result.unwrapOr (метод)Result.andThen (метод)Result.asyncAndThen (метод)Result.orElse (метод)Result.match (метод)Result.asyncMap (метод)Result.andTee (метод)Result.andThrough (метод)Result.asyncAndThrough (метод)Result.fromThrowable (метод статического класса)Result.combine (метод статического класса)Result.combineWithAllErrors (метод статического класса)Result.safeUnwrap()ResultAsync )okAsyncerrAsyncResultAsync.fromThrowable (метод статического класса)ResultAsync.fromPromise (метод статического класса)ResultAsync.fromSafePromise (метод статического класса)ResultAsync.map (метод)ResultAsync.mapErr (метод)ResultAsync.unwrapOr (метод)ResultAsync.andThen (метод)ResultAsync.orElse (Метод)ResultAsync.match (метод)ResultAsync.andTee (метод)ResultAsync.andThrough (метод)ResultAsync.combine (метод статического класса)ResultAsync.combineWithAllErrors (метод статического класса)ResultAsync.safeUnwrap()fromThrowablefromAsyncThrowablefromPromisefromSafePromisesafeTry > npm install neverthroweslint-plugin-neverthrow В рамках программы neverthrow S Bounty, пользователь Mdbetancourt создал eslint-plugin-neverthrow чтобы гарантировать, что ошибки не исчезли.
Установите, запустив:
> npm install eslint-plugin-neverthrow С eslint-plugin-neverthrow вы вынуждены потреблять результат одним из следующих трех способов:
.match.unwrapOr._unsafeUnwrap Это гарантирует, что вы явно обрабатываете ошибку вашего Result .
Этот плагин по сути представляет собой портирование атрибута, must-use .
neverthrow раскрывает следующее:
ok удобная функция, чтобы создать Ok -вариант Resulterr для создания варианта Err ResultOk класс и типErr Class и типResult , а также пространство имен / объект, с которого Result.fromThrowable вызовать.ResultAsync КЛАССokAsync для создания ResultAsync , содержащего Result типа OkerrAsync для создания ResultAsync , содержащего Result типа Err import {
ok ,
Ok ,
err ,
Err ,
Result ,
okAsync ,
errAsync ,
ResultAsync ,
fromAsyncThrowable ,
fromThrowable ,
fromPromise ,
fromSafePromise ,
safeTry ,
} from 'neverthrow' Проверьте вики для помощи, как максимально использовать neverthrow .
Если вы найдете этот пакет полезным, пожалуйста, подумайте о том, чтобы спонсировать меня или просто купить мне кофе!
Result ) ok Создает Ok Variant Result
Подпись:
ok < T , E > ( value : T ) : Ok < T , E > { ... }Пример:
import { ok } from 'neverthrow'
const myResult = ok ( { myData : 'test' } ) // instance of `Ok`
myResult . isOk ( ) // true
myResult . isErr ( ) // false⬆ Вернуться к вершине
err Создает вариант Err Result
Подпись:
err < T , E > ( error : E ) : Err < T , E > { ... }Пример:
import { err } from 'neverthrow'
const myResult = err ( 'Oh noooo' ) // instance of `Err`
myResult . isOk ( ) // false
myResult . isErr ( ) // true⬆ Вернуться к вершине
Result.isOk (метод) Возвращает true если результат является Ok Variant
Подпись:
isOk ( ) : boolean { ... }⬆ Вернуться к вершине
Result.isErr (метод) Возвращает true , если результат является вариантом Err
Подпись :
isErr ( ) : boolean { ... }⬆ Вернуться к вершине
Result.map (метод) Карты Result<T, E> для Result<U, E> применив функцию к содержащему значения Ok , оставляя значение Err нетронутым.
Эта функция может быть использована для составления результатов двух функций.
Подпись:
class Result < T , E > {
map < U > ( callback : ( value : T ) => U ) : Result < U , E > { ... }
}Пример :
import { getLines } from 'imaginary-parser'
// ^ assume getLines has the following signature:
// getLines(str: string): Result<Array<string>, Error>
// since the formatting is deemed correct by `getLines`
// then it means that `linesResult` is an Ok
// containing an Array of strings for each line of code
const linesResult = getLines ( '1n2n3n4n' )
// this Result now has a Array<number> inside it
const newResult = linesResult . map (
( arr : Array < string > ) => arr . map ( parseInt )
)
newResult . isOk ( ) // true⬆ Вернуться к вершине
Result.mapErr (метод) Карты Result<T, E> для Result<T, F> применив функцию к содержащему значение Err , оставляя значение Ok нетронутым.
Эта функция может использоваться для прохождения успешного результата при обработке ошибки.
Подпись:
class Result < T , E > {
mapErr < F > ( callback : ( error : E ) => F ) : Result < T , F > { ... }
}Пример :
import { parseHeaders } from 'imaginary-http-parser'
// imagine that parseHeaders has the following signature:
// parseHeaders(raw: string): Result<SomeKeyValueMap, ParseError>
const rawHeaders = 'nonsensical gibberish and badly formatted stuff'
const parseResult = parseHeaders ( rawHeaders )
parseResult . mapErr ( parseError => {
res . status ( 400 ) . json ( {
error : parseError
} )
} )
parseResult . isErr ( ) // true⬆ Вернуться к вершине
Result.unwrapOr (метод) Развернуть значение Ok или вернуть по умолчанию, если есть Err
Подпись:
class Result < T , E > {
unwrapOr < T > ( value : T ) : T { ... }
}Пример :
const myResult = err ( 'Oh noooo' )
const multiply = ( value : number ) : number => value * 2
const unwrapped : number = myResult . map ( multiply ) . unwrapOr ( 10 )⬆ Вернуться к вершине
Result.andThen (метод) Та же идея, что и map выше. За исключением того, что вы должны вернуть новый Result .
Возвращенное значение будет Result . По состоянию на v4.1.0-beta вы можете вернуть различные типы ошибок (см. Подпись ниже). До v4.1.0-beta тип ошибки не может быть различен.
Это полезно, когда вам нужно сделать последующее вычисление, используя внутреннее значение T , но это вычисление может потерпеть неудачу.
Кроме того, andThen действительно полезно в качестве инструмента для выравнивания Result<Result<A, E2>, E1> в Result<A, E2> (см. Пример ниже).
Подпись:
class Result < T , E > {
// Note that the latest version lets you return distinct errors as well.
// If the error types (E and F) are the same (like `string | string`)
// then they will be merged into one type (`string`)
andThen < U , F > (
callback : ( value : T ) => Result < U , F >
) : Result < U , E | F > { ... }
}Пример 1: Результаты цепочки
import { err , ok } from 'neverthrow'
const sq = ( n : number ) : Result < number , number > => ok ( n ** 2 )
ok ( 2 )
. andThen ( sq )
. andThen ( sq ) // Ok(16)
ok ( 2 )
. andThen ( sq )
. andThen ( err ) // Err(4)
ok ( 2 )
. andThen ( err )
. andThen ( sq ) // Err(2)
err ( 3 )
. andThen ( sq )
. andThen ( sq ) // Err(3)Пример 2: сглаживание вложенных результатов
// It's common to have nested Results
const nested = ok ( ok ( 1234 ) )
// notNested is a Ok(1234)
const notNested = nested . andThen ( ( innerResult ) => innerResult )⬆ Вернуться к вершине
Result.asyncAndThen (метод) Такая же идея, что andThen выше, за исключением того, что вы должны вернуть новый ResultAsync .
Возвращенное значение будет ResultAsync .
Подпись:
class Result < T , E > {
asyncAndThen < U , F > (
callback : ( value : T ) => ResultAsync < U , F >
) : ResultAsync < U , E | F > { ... }
}⬆ Вернуться к вершине
Result.orElse (метод) Принимает значение Err и отображает его в Result<T, SomeNewType> . Это полезно для восстановления ошибки.
Подпись:
class Result < T , E > {
orElse < U , A > (
callback : ( error : E ) => Result < U , A >
) : Result < U | T , A > { ... }
}Пример:
enum DatabaseError {
PoolExhausted = 'PoolExhausted' ,
NotFound = 'NotFound' ,
}
const dbQueryResult : Result < string , DatabaseError > = err ( DatabaseError . NotFound )
const updatedQueryResult = dbQueryResult . orElse ( ( dbError ) =>
dbError === DatabaseError . NotFound
? ok ( 'User does not exist' ) // error recovery branch: ok() must be called with a value of type string
//
//
// err() can be called with a value of any new type that you want
// it could also be called with the same error value
//
// err(dbError)
: err ( 500 )
)⬆ Вернуться к вершине
Result.match (метод) Учитывая 2 функции (одна для варианта Ok и одна для варианта Err ) выполняет функцию, которая соответствует варианту Result .
Совместные обратные вызовы не требуют возврата Result , однако вы можете вернуть Result если хотите.
Подпись:
class Result < T , E > {
match < A , B = A > (
okCallback : ( value : T ) => A ,
errorCallback : ( error : E ) => B
) : A | B => { ... }
} match похоже на map цепочки и mapErr , с тем, что с match обе функции должны иметь одинаковый тип возврата. Различия между map match и цепочки и mapErr : это:
match обе функции должны иметь одинаковый возврат типа Amatch Result<T, E> в A (тип возврата функций совпадений)Пример:
// map/mapErr api
// note that you DON'T have to append mapErr
// after map which means that you are not required to do
// error handling
computationThatMightFail ( ) . map ( console . log ) . mapErr ( console . error )
// match api
// works exactly the same as above since both callbacks
// only perform side effects,
// except, now you HAVE to do error handling :)
computationThatMightFail ( ) . match ( console . log , console . error )
// Returning values
const attempt = computationThatMightFail ( )
. map ( ( str ) => str . toUpperCase ( ) )
. mapErr ( ( err ) => `Error: ${ err } ` )
// `attempt` is of type `Result<string, string>`
const answer = computationThatMightFail ( ) . match (
( str ) => str . toUpperCase ( ) ,
( err ) => `Error: ${ err } `
)
// `answer` is of type `string` Если вы не используете параметр ошибки в обратном вызове соответствия, то match , эквивалентна map цепочки с unwrapOr :
const answer = computationThatMightFail ( ) . match (
( str ) => str . toUpperCase ( ) ,
( ) => 'ComputationError'
)
// `answer` is of type `string`
const answer = computationThatMightFail ( )
. map ( ( str ) => str . toUpperCase ( ) )
. unwrapOr ( 'ComputationError' )⬆ Вернуться к вершине
Result.asyncMap (метод) Похоже на map , за исключением двух вещей:
PromiseResultAsync Затем вы можете подготовить результат asyncMap используя API -интерфейсы ResultAsync (например, map , mapErr , andThen и т. Д.)
Подпись:
class Result < T , E > {
asyncMap < U > (
callback : ( value : T ) => Promise < U >
) : ResultAsync < U , E > { ... }
}Пример:
import { parseHeaders } from 'imaginary-http-parser'
// imagine that parseHeaders has the following signature:
// parseHeaders(raw: string): Result<SomeKeyValueMap, ParseError>
const asyncRes = parseHeaders ( rawHeader )
. map ( headerKvMap => headerKvMap . Authorization )
. asyncMap ( findUserInDatabase ) Обратите внимание, что в приведенном выше примере, если parseHeaders возвращает Err , то .map и .asyncMap не будут вызваны, а переменная asyncRes будет разрешена до Err , когда превратится в Result , используя await или .then() .
⬆ Вернуться к вершине
Result.andTee (метод) Получает Result<T, E> и позволяет исходному Result<T, E> пройти через себя независимо от результата передачи функции. Это удобный способ обработки побочных эффектов, неудача или успех которых не должны влиять на вашу основную логику, такую как ведение журнала.
Подпись:
class Result < T , E > {
andTee (
callback : ( value : T ) => unknown
) : Result < T , E > { ... }
}Пример:
import { parseUserInput } from 'imaginary-parser'
import { logUser } from 'imaginary-logger'
import { insertUser } from 'imaginary-database'
// ^ assume parseUserInput, logUser and insertUser have the following signatures:
// parseUserInput(input: RequestData): Result<User, ParseError>
// logUser(user: User): Result<void, LogError>
// insertUser(user: User): ResultAsync<void, InsertError>
// Note logUser returns void upon success but insertUser takes User type.
const resAsync = parseUserInput ( userInput )
. andTee ( logUser )
. asyncAndThen ( insertUser )
// Note no LogError shows up in the Result type
resAsync . then ( ( res : Result < void , ParseError | InsertError > ) => { e
if ( res . isErr ( ) ) {
console . log ( "Oops, at least one step failed" , res . error )
}
else {
console . log ( "User input has been parsed and inserted successfully." )
}
} ) )⬆ Вернуться к вершине
Result.andThrough (метод) Похоже на andTee кроме:
Подпись:
class Result < T , E > {
andThrough < F > (
callback : ( value : T ) => Result < unknown , F >
) : Result < T , E | F > { ... }
}Пример:
import { parseUserInput } from 'imaginary-parser'
import { validateUser } from 'imaginary-validator'
import { insertUser } from 'imaginary-database'
// ^ assume parseUseInput, validateUser and insertUser have the following signatures:
// parseUserInput(input: RequestData): Result<User, ParseError>
// validateUser(user: User): Result<void, ValidateError>
// insertUser(user: User): ResultAsync<void, InsertError>
// Note validateUser returns void upon success but insertUser takes User type.
const resAsync = parseUserInput ( userInput )
. andThrough ( validateUser )
. asyncAndThen ( insertUser )
resAsync . then ( ( res : Result < void , ParseErro | ValidateError | InsertError > ) => { e
if ( res . isErr ( ) ) {
console . log ( "Oops, at least one step failed" , res . error )
}
else {
console . log ( "User input has been parsed, validated, inserted successfully." )
}
} ) )⬆ Вернуться к вершине
Result.asyncAndThrough (метод) Похоже на andThrough вы можете вернуть результатсинс.
Затем вы можете подготовить результат asyncAndThrough используя API -интерфейсы ResultAsync (например, map , mapErr , andThen и т. Д.)
Подпись:
import { parseUserInput } from 'imaginary-parser'
import { insertUser } from 'imaginary-database'
import { sendNotification } from 'imaginary-service'
// ^ assume parseUserInput, insertUser and sendNotification have the following signatures:
// parseUserInput(input: RequestData): Result<User, ParseError>
// insertUser(user: User): ResultAsync<void, InsertError>
// sendNotification(user: User): ResultAsync<void, NotificationError>
// Note insertUser returns void upon success but sendNotification takes User type.
const resAsync = parseUserInput ( userInput )
. asyncAndThrough ( insertUser )
. andThen ( sendNotification )
resAsync . then ( ( res : Result < void , ParseError | InsertError | NotificationError > ) => { e
if ( res . isErr ( ) ) {
console . log ( "Oops, at least one step failed" , res . error )
}
else {
console . log ( "User has been parsed, inserted and notified successfully." )
}
} ) )⬆ Вернуться к вершине
Result.fromThrowable (метод статического класса)Хотя результат не является фактическим классом JS, способ реализации
fromThrowableтребует от вызыванияfromThrowableкак если бы это был статический метод вResult. Смотрите примеры ниже.
Сообщество JavaScript согласилось с конвенцией о том, чтобы бросить исключения. Таким образом, при взаимодействии со сторонними библиотеками необходимо обернуть сторонний код в блоки Try / Catch.
Эта функция создаст новую функцию, которая возвращает Err , когда бросает исходную функцию.
Невозможно узнать типы ошибок, брошенных в исходную функцию, поэтому рекомендуется использовать второй аргумент errorFn для сопоставления того, что брошено на известный тип.
Пример :
import { Result } from 'neverthrow'
type ParseError = { message : string }
const toParseError = ( ) : ParseError => ( { message : "Parse Error" } )
const safeJsonParse = Result . fromThrowable ( JSON . parse , toParseError )
// the function can now be used safely, if the function throws, the result will be an Err
const res = safeJsonParse ( "{" ) ;⬆ Вернуться к вершине
Result.combine (метод статического класса)Хотя результат не является фактическим классом JS, способ реализации
combineтребует, чтобы вы называлиcombineкак если бы это был статический метод вResult. Смотрите примеры ниже.
Объединить списки Result с.
Если вы знакомы с Promise.all Все, функция комбинирования работает концептуально одинаково.
combine работы как в гетерогенных, так и однородных списках . Это означает, что у вас могут быть списки, которые содержат различные виды Result и все еще могут их объединить. Обратите внимание, что вы не можете объединить списки, которые содержат как Result , так и ResultAsync .
Функция комбинирования принимает список результатов и возвращает один результат. Если все результаты в списке в Ok , то возвратное значение будет Ok содержащим список всех отдельных значений Ok .
Если только один из результатов в списке является Err , то функция комбинирования возвращает это значение ошибки (оно короткие замыкания и возвращает первую ошибку, которую она находит).
Формально говоря:
// homogeneous lists
function combine < T , E > ( resultList : Result < T , E > [ ] ) : Result < T [ ] , E >
// heterogeneous lists
function combine < T1 , T2 , E1 , E2 > ( resultList : [ Result < T1 , E1 > , Result < T2 , E2 > ] ) : Result < [ T1 , T2 ] , E1 | E2 >
function combine < T1 , T2 , T3 , E1 , E2 , E3 > => Result < [ T1 , T2 , T3 ] , E1 | E2 | E3 >
function combine < T1 , T2 , T3 , T4 , E1 , E2 , E3 , E4 > => Result < [ T1 , T2 , T3 , T4 ] , E1 | E2 | E3 | E4 >
// ... etc etc ad infinitumПример:
const resultList : Result < number , never > [ ] =
[ ok ( 1 ) , ok ( 2 ) ]
const combinedList : Result < number [ ] , unknown > =
Result . combine ( resultList )Пример с помощью кортежей:
/** @example tuple(1, 2, 3) === [1, 2, 3] // with type [number, number, number] */
const tuple = < T extends any [ ] > ( ... args : T ) : T => args
const resultTuple : [ Result < string , never > , Result < string , never > ] =
tuple ( ok ( 'a' ) , ok ( 'b' ) )
const combinedTuple : Result < [ string , string ] , unknown > =
Result . combine ( resultTuple )⬆ Вернуться к вершине
Result.combineWithAllErrors (метод статического класса)Хотя результат не является фактическим классом JS, способ реализации
combineWithAllErrorsтребует, чтобы вы называлиcombineWithAllErrorsкак если бы это был статический метод вResult. Смотрите примеры ниже.
Как combine , но без короткого замыкания. Вместо просто первого значения ошибки вы получаете список всех значений ошибок в списке результатов ввода.
Если только некоторые результаты не удастся, новый комбинированный список ошибок будет содержать только значение ошибки неудачных результатов, что означает, что нет никакой гарантии длины нового списка ошибок.
Функциональная подпись:
// homogeneous lists
function combineWithAllErrors < T , E > ( resultList : Result < T , E > [ ] ) : Result < T [ ] , E [ ] >
// heterogeneous lists
function combineWithAllErrors < T1 , T2 , E1 , E2 > ( resultList : [ Result < T1 , E1 > , Result < T2 , E2 > ] ) : Result < [ T1 , T2 ] , ( E1 | E2 ) [ ] >
function combineWithAllErrors < T1 , T2 , T3 , E1 , E2 , E3 > => Result < [ T1 , T2 , T3 ] , ( E1 | E2 | E3 ) [ ] >
function combineWithAllErrors < T1 , T2 , T3 , T4 , E1 , E2 , E3 , E4 > => Result < [ T1 , T2 , T3 , T4 ] , ( E1 | E2 | E3 | E4 ) [ ] >
// ... etc etc ad infinitumПример использования:
const resultList : Result < number , string > [ ] = [
ok ( 123 ) ,
err ( 'boooom!' ) ,
ok ( 456 ) ,
err ( 'ahhhhh!' ) ,
]
const result = Result . combineWithAllErrors ( resultList )
// result is Err(['boooom!', 'ahhhhh!'])⬆ Вернуться к вершине
Result.safeUnwrap()Устарел . Вам больше не нужно использовать этот метод.
Позволяет развернуть Result или неявно возвращать Err , тем самым уменьшая шаблон.
⬆ Вернуться к вершине
ResultAsync ) okAsync Создает Ok VARIANT ResultAsync
Подпись:
okAsync < T , E > ( value : T ) : ResultAsync < T , E >Пример:
import { okAsync } from 'neverthrow'
const myResultAsync = okAsync ( { myData : 'test' } ) // instance of `ResultAsync`
const myResult = await myResultAsync // instance of `Ok`
myResult . isOk ( ) // true
myResult . isErr ( ) // false⬆ Вернуться к вершине
errAsync Создает вариант Err ResultAsync
Подпись:
errAsync < T , E > ( error : E ) : ResultAsync < T , E >Пример:
import { errAsync } from 'neverthrow'
const myResultAsync = errAsync ( 'Oh nooo' ) // instance of `ResultAsync`
const myResult = await myResultAsync // instance of `Err`
myResult . isOk ( ) // false
myResult . isErr ( ) // true⬆ Вернуться к вершине
ResultAsync.fromThrowable (метод статического класса) Аналогично результату Promise
Пример :
import { ResultAsync } from 'neverthrow'
import { insertIntoDb } from 'imaginary-database'
// insertIntoDb(user: User): Promise<User>
const insertUser = ResultAsync . fromThrowable ( insertIntoDb , ( ) => new Error ( 'Database error' ) )
// `res` has a type of (user: User) => ResultAsync<User, Error> Обратите внимание, что это может быть более безопасным, чем использование ResultAsync.fromise с результатом вызова функции, потому что не все функции, которые возвращают Promise являются async , и, таким образом, они могут бросать ошибки синхронно, а не возвращать отвергнутое Promise . Например:
// NOT SAFE !!
import { ResultAsync } from 'neverthrow'
import { db } from 'imaginary-database'
// db.insert<T>(table: string, value: T): Promise<T>
const insertUser = ( user : User ) : Promise < User > => {
if ( ! user . id ) {
// this throws synchronously!
throw new TypeError ( 'missing user id' )
}
return db . insert ( 'users' , user )
}
// this will throw, NOT return a `ResultAsync`
const res = ResultAsync . fromPromise ( insertIntoDb ( myUser ) , ( ) => new Error ( 'Database error' ) )⬆ Вернуться к вершине
ResultAsync.fromPromise (метод статического класса) Преобразует PromiseLike<T> (которые могут бросить) в ResultAsync<T, E> .
Второй аргумент обрабатывает случай отказа обещания и отображает ошибку из unknown в какой -то тип E .
Подпись:
// fromPromise is a static class method
// also available as a standalone function
// import { fromPromise } from 'neverthrow'
ResultAsync . fromPromise < T , E > (
promise : PromiseLike < T > ,
errorHandler : ( unknownError : unknown ) => E )
) : ResultAsync < T , E > { ... } Если вы работаете с PromiseLike Objects, которые, как вы знаете, не будет бросить, то используйте fromSafePromise , чтобы избежать необходимости передавать избыточный аргумент errorHandler .
Пример :
import { ResultAsync } from 'neverthrow'
import { insertIntoDb } from 'imaginary-database'
// insertIntoDb(user: User): Promise<User>
const res = ResultAsync . fromPromise ( insertIntoDb ( myUser ) , ( ) => new Error ( 'Database error' ) )
// `res` has a type of ResultAsync<User, Error>⬆ Вернуться к вершине
ResultAsync.fromSafePromise (метод статического класса) То же самое, что ResultAsync.fromPromise за исключением того, что он не обрабатывает отказ от обещания. Убедитесь, что вы знаете, что вы делаете, в противном случае исключение в рамках этого обещания приведет к тому, что результат может отказаться, а не решить в результате.
Подпись:
// fromPromise is a static class method
// also available as a standalone function
// import { fromPromise } from 'neverthrow'
ResultAsync . fromSafePromise < T , E > (
promise : PromiseLike < T >
) : ResultAsync < T , E > { ... }Пример :
import { RouteError } from 'routes/error'
// simulate slow routes in an http server that works in a Result / ResultAsync context
// Adopted from https://github.com/parlez-vous/server/blob/2496bacf55a2acbebc30631b5562f34272794d76/src/routes/common/signup.ts
export const slowDown = < T > ( ms : number ) => ( value : T ) =>
ResultAsync . fromSafePromise < T , RouteError > (
new Promise ( ( resolve ) => {
setTimeout ( ( ) => {
resolve ( value )
} , ms )
} )
)
export const signupHandler = route < User > ( ( req , sessionManager ) =>
decode ( userSignupDecoder , req . body , 'Invalid request body' ) . map ( ( parsed ) => {
return createUser ( parsed )
. andThen ( slowDown ( 3000 ) ) // slowdown by 3 seconds
. andThen ( sessionManager . createSession )
. map ( ( { sessionToken , admin } ) => AppData . init ( admin , sessionToken ) )
} )
)⬆ Вернуться к вершине
ResultAsync.map (метод) Карты ResultAsync<T, E> для ResultAsync<U, E> применив функцию к содержащему значение Ok , оставляя значение Err нетронутым.
Применяемая функция может быть синхронной или асинхронной (возвращая Promise<U> ) без воздействия на тип возврата.
Эта функция может быть использована для составления результатов двух функций.
Подпись:
class ResultAsync < T , E > {
map < U > (
callback : ( value : T ) => U | Promise < U >
) : ResultAsync < U , E > { ... }
}Пример :
import { findUsersIn } from 'imaginary-database'
// ^ assume findUsersIn has the following signature:
// findUsersIn(country: string): ResultAsync<Array<User>, Error>
const usersInCanada = findUsersIn ( "Canada" )
// Let's assume we only need their names
const namesInCanada = usersInCanada . map ( ( users : Array < User > ) => users . map ( user => user . name ) )
// namesInCanada is of type ResultAsync<Array<string>, Error>
// We can extract the Result using .then() or await
namesInCanada . then ( ( namesResult : Result < Array < string > , Error > ) => {
if ( namesResult . isErr ( ) ) {
console . log ( "Couldn't get the users from the database" , namesResult . error )
}
else {
console . log ( "Users in Canada are named: " + namesResult . value . join ( ',' ) )
}
} )⬆ Вернуться к вершине
ResultAsync.mapErr (метод) Карты ResultAsync<T, E> чтобы ResultAsync<T, F> применив функцию к содержащему значение Err , оставив значение Ok нетронутым.
Применяемая функция может быть синхронной или асинхронной (возвращая Promise<F> ) без воздействия на тип возврата.
Эта функция может использоваться для прохождения успешного результата при обработке ошибки.
Подпись:
class ResultAsync < T , E > {
mapErr < F > (
callback : ( error : E ) => F | Promise < F >
) : ResultAsync < T , F > { ... }
}Пример :
import { findUsersIn } from 'imaginary-database'
// ^ assume findUsersIn has the following signature:
// findUsersIn(country: string): ResultAsync<Array<User>, Error>
// Let's say we need to low-level errors from findUsersIn to be more readable
const usersInCanada = findUsersIn ( "Canada" ) . mapErr ( ( error : Error ) => {
// The only error we want to pass to the user is "Unknown country"
if ( error . message === "Unknown country" ) {
return error . message
}
// All other errors will be labelled as a system error
return "System error, please contact an administrator."
} )
// usersInCanada is of type ResultAsync<Array<User>, string>
usersInCanada . then ( ( usersResult : Result < Array < User > , string > ) => {
if ( usersResult . isErr ( ) ) {
res . status ( 400 ) . json ( {
error : usersResult . error
} )
}
else {
res . status ( 200 ) . json ( {
users : usersResult . value
} )
}
} )⬆ Вернуться к вершине
ResultAsync.unwrapOr (метод) Развернуть значение Ok или вернуть по умолчанию, если есть Err .
Работает так же, как Result.unwrapOr , но возвращает Promise<T> вместо T .
Подпись:
class ResultAsync < T , E > {
unwrapOr < T > ( value : T ) : Promise < T > { ... }
}Пример :
const unwrapped : number = await errAsync ( 0 ) . unwrapOr ( 10 )
// unwrapped = 10⬆ Вернуться к вершине
ResultAsync.andThen (метод) Та же идея, что и map выше. За исключением применяемой функции, должна вернуть Result или ResultAsync .
ResultAsync.andThen и всегда возвращает ResultAsync независимо от типа возврата применяемой функции.
Это полезно, когда вам нужно сделать последующее вычисление, используя внутреннее значение T , но это вычисление может потерпеть неудачу.
andThen действительно полезен в качестве инструмента для выравнивания ResultAsync<ResultAsync<A, E2>, E1> в ResultAsync<A, E2> (см. Пример ниже).
Подпись:
// Note that the latest version (v4.1.0-beta) lets you return distinct errors as well.
// If the error types (E and F) are the same (like `string | string`)
// then they will be merged into one type (`string`)
class ResultAsync < T , E > {
andThen < U , F > (
callback : ( value : T ) => Result < U , F > | ResultAsync < U , F >
) : ResultAsync < U , E | F > { ... }
}Пример
import { validateUser } from 'imaginary-validator'
import { insertUser } from 'imaginary-database'
import { sendNotification } from 'imaginary-service'
// ^ assume validateUser, insertUser and sendNotification have the following signatures:
// validateUser(user: User): Result<User, Error>
// insertUser(user): ResultAsync<User, Error>
// sendNotification(user): ResultAsync<void, Error>
const resAsync = validateUser ( user )
. andThen ( insertUser )
. andThen ( sendNotification )
// resAsync is a ResultAsync<void, Error>
resAsync . then ( ( res : Result < void , Error > ) => {
if ( res . isErr ( ) ) {
console . log ( "Oops, at least one step failed" , res . error )
}
else {
console . log ( "User has been validated, inserted and notified successfully." )
}
} )⬆ Вернуться к вершине
ResultAsync.orElse (Метод) Принимает значение Err и отображает его с ResultAsync<T, SomeNewType> . Это полезно для восстановления ошибки.
Подпись:
class ResultAsync < T , E > {
orElse < U , A > (
callback : ( error : E ) => Result < U , A > | ResultAsync < U , A >
) : ResultAsync < U | T , A > { ... }
}⬆ Вернуться к вершине
ResultAsync.match (метод) Учитывая 2 функции (одна для варианта Ok и одна для варианта Err ) выполняет функцию, которая соответствует варианту ResultAsync .
Разница с Result.match заключается в том, что она всегда возвращает Promise из -за асинхронной природы ResultAsync .
Подпись:
class ResultAsync < T , E > {
match < A , B = A > (
okCallback : ( value : T ) => A ,
errorCallback : ( error : E ) => B
) : Promise < A | B > => { ... }
}Пример:
import { validateUser } from 'imaginary-validator'
import { insertUser } from 'imaginary-database'
// ^ assume validateUser and insertUser have the following signatures:
// validateUser(user: User): Result<User, Error>
// insertUser(user): ResultAsync<User, Error>
// Handle both cases at the end of the chain using match
const resultMessage = await validateUser ( user )
. andThen ( insertUser )
. match (
( user : User ) => `User ${ user . name } has been successfully created` ,
( error : Error ) => `User could not be created because ${ error . message } `
)
// resultMessage is a string⬆ Вернуться к вершине
ResultAsync.andTee (метод) Берет ResultAsync<T, E> и позволяет исходному ResultAsync<T, E> пройти через себя независимо от результата передачи функции. Это удобный способ обработки побочных эффектов, неудача или успех которых не должны влиять на вашу основную логику, такую как ведение журнала.
Подпись:
class ResultAsync < T , E > {
andTee (
callback : ( value : T ) => unknown
) : ResultAsync < T , E > => { ... }
}Пример:
import { insertUser } from 'imaginary-database'
import { logUser } from 'imaginary-logger'
import { sendNotification } from 'imaginary-service'
// ^ assume insertUser, logUser and sendNotification have the following signatures:
// insertUser(user: User): ResultAsync<User, InsertError>
// logUser(user: User): Result<void, LogError>
// sendNotification(user: User): ResultAsync<void, NotificationError>
// Note logUser returns void on success but sendNotification takes User type.
const resAsync = insertUser ( user )
. andTee ( logUser )
. andThen ( sendNotification )
// Note there is no LogError in the types below
resAsync . then ( ( res : Result < void , InsertError | NotificationError > ) => { e
if ( res . isErr ( ) ) {
console . log ( "Oops, at least one step failed" , res . error )
}
else {
console . log ( "User has been inserted and notified successfully." )
}
} ) ) ⬆ Вернуться к вершине
ResultAsync.andThrough (метод) Похоже на andTee кроме:
Подпись:
class ResultAsync < T , E > {
andThrough < F > (
callback : ( value : T ) => Result < unknown , F > | ResultAsync < unknown , F > ,
) : ResultAsync < T , E | F > => { ... }
}Пример:
import { buildUser } from 'imaginary-builder'
import { insertUser } from 'imaginary-database'
import { sendNotification } from 'imaginary-service'
// ^ assume buildUser, insertUser and sendNotification have the following signatures:
// buildUser(userRaw: UserRaw): ResultAsync<User, BuildError>
// insertUser(user: User): ResultAsync<void, InsertError>
// sendNotification(user: User): ResultAsync<void, NotificationError>
// Note insertUser returns void upon success but sendNotification takes User type.
const resAsync = buildUser ( userRaw )
. andThrough ( insertUser )
. andThen ( sendNotification )
resAsync . then ( ( res : Result < void , BuildError | InsertError | NotificationError > ) => { e
if ( res . isErr ( ) ) {
console . log ( "Oops, at least one step failed" , res . error )
}
else {
console . log ( "User data has been built, inserted and notified successfully." )
}
} ) ) ⬆ Вернуться к вершине
ResultAsync.combine (метод статического класса) Объединить списки ResultAsync .
Если вы знакомы с Promise.all Все, функция комбинирования работает концептуально одинаково.
combine работы как в гетерогенных, так и однородных списках . Это означает, что у вас могут быть списки, которые содержат различные виды ResultAsync , и все же могут их объединить. Обратите внимание, что вы не можете объединить списки, которые содержат как Result , так и ResultAsync .
Функция комбинирования принимает список результатов и возвращает один результат. Если все результаты в списке в Ok , то возвратное значение будет Ok содержащим список всех отдельных значений Ok .
Если только один из результатов в списке является Err , то функция комбинирования возвращает это значение ошибки (оно короткие замыкания и возвращает первую ошибку, которую она находит).
Формально говоря:
// homogeneous lists
function combine < T , E > ( resultList : ResultAsync < T , E > [ ] ) : ResultAsync < T [ ] , E >
// heterogeneous lists
function combine < T1 , T2 , E1 , E2 > ( resultList : [ ResultAsync < T1 , E1 > , ResultAsync < T2 , E2 > ] ) : ResultAsync < [ T1 , T2 ] , E1 | E2 >
function combine < T1 , T2 , T3 , E1 , E2 , E3 > => ResultAsync < [ T1 , T2 , T3 ] , E1 | E2 | E3 >
function combine < T1 , T2 , T3 , T4 , E1 , E2 , E3 , E4 > => ResultAsync < [ T1 , T2 , T3 , T4 ] , E1 | E2 | E3 | E4 >
// ... etc etc ad infinitumПример:
const resultList : ResultAsync < number , never > [ ] =
[ okAsync ( 1 ) , okAsync ( 2 ) ]
const combinedList : ResultAsync < number [ ] , unknown > =
ResultAsync . combine ( resultList )Пример с помощью кортежей:
/** @example tuple(1, 2, 3) === [1, 2, 3] // with type [number, number, number] */
const tuple = < T extends any [ ] > ( ... args : T ) : T => args
const resultTuple : [ ResultAsync < string , never > , ResultAsync < string , never > ] =
tuple ( okAsync ( 'a' ) , okAsync ( 'b' ) )
const combinedTuple : ResultAsync < [ string , string ] , unknown > =
ResultAsync . combine ( resultTuple )⬆ Вернуться к вершине
ResultAsync.combineWithAllErrors (метод статического класса) Как combine , но без короткого замыкания. Вместо просто первого значения ошибки вы получаете список всех значений ошибок в списке результатов ввода.
Если только некоторые результаты не удастся, новый комбинированный список ошибок будет содержать только значение ошибки неудачных результатов, что означает, что нет никакой гарантии длины нового списка ошибок.
Функциональная подпись:
// homogeneous lists
function combineWithAllErrors < T , E > ( resultList : ResultAsync < T , E > [ ] ) : ResultAsync < T [ ] , E [ ] >
// heterogeneous lists
function combineWithAllErrors < T1 , T2 , E1 , E2 > ( resultList : [ ResultAsync < T1 , E1 > , ResultAsync < T2 , E2 > ] ) : ResultAsync < [ T1 , T2 ] , ( E1 | E2 ) [ ] >
function combineWithAllErrors < T1 , T2 , T3 , E1 , E2 , E3 > => ResultAsync < [ T1 , T2 , T3 ] , ( E1 | E2 | E3 ) [ ] >
function combineWithAllErrors < T1 , T2 , T3 , T4 , E1 , E2 , E3 , E4 > => ResultAsync < [ T1 , T2 , T3 , T4 ] , ( E1 | E2 | E3 | E4 ) [ ] >
// ... etc etc ad infinitumПример использования:
const resultList : ResultAsync < number , string > [ ] = [
okAsync ( 123 ) ,
errAsync ( 'boooom!' ) ,
okAsync ( 456 ) ,
errAsync ( 'ahhhhh!' ) ,
]
const result = ResultAsync . combineWithAllErrors ( resultList )
// result is Err(['boooom!', 'ahhhhh!']) ResultAsync.safeUnwrap()Устарел . Вам больше не нужно использовать этот метод.
Позволяет развернуть Result или неявно возвращать Err , тем самым уменьшая шаблон.
⬆ Вернуться к вершине
fromThrowable Экспорт Result.fromThrowable верхнего уровня. Пожалуйста, найдите документацию в результате.
⬆ Вернуться к вершине
fromAsyncThrowable Экспорт ResultAsync.fromThrowable . Пожалуйста, найдите документацию в Resultasync.fromthrowable
⬆ Вернуться к вершине
fromPromise Экспорт ResultAsync.fromPromise . Пожалуйста, найдите документацию в Resultasync.frompromise
⬆ Вернуться к вершине
fromSafePromise Экспорт ResultAsync.fromSafePromise на верхнем уровне. Пожалуйста, найдите документацию в Resultasync.fromasafepromise
⬆ Вернуться к вершине
safeTryИспользуется для неявного возврата ошибок и уменьшения шаблона.
Допустим, мы пишем функцию, которая возвращает Result , и в этой функции мы называем некоторые функции, которые также возвращают Result , и мы проверяем эти результаты, чтобы увидеть, должны ли мы продолжать идти или прервать. Обычно мы пишем следующее.
declare function mayFail1 ( ) : Result < number , string > ;
declare function mayFail2 ( ) : Result < number , string > ;
function myFunc ( ) : Result < number , string > {
// We have to define a constant to hold the result to check and unwrap its value.
const result1 = mayFail1 ( ) ;
if ( result1 . isErr ( ) ) {
return err ( `aborted by an error from 1st function, ${ result1 . error } ` ) ;
}
const value1 = result1 . value
// Again, we need to define a constant and then check and unwrap.
const result2 = mayFail2 ( ) ;
if ( result2 . isErr ( ) ) {
return err ( `aborted by an error from 2nd function, ${ result2 . error } ` ) ;
}
const value2 = result2 . value
// And finally we return what we want to calculate
return ok ( value1 + value2 ) ;
} По сути, нам нужно определить константу для каждого результата, чтобы проверить, является ли он Ok и прочитать его .value или .error .
С помощью Safetry мы можем указать «вернуться сюда, если это Err , иначе развернуть ее здесь и продолжать». Всего в одном выражении.
declare function mayFail1 ( ) : Result < number , string > ;
declare function mayFail2 ( ) : Result < number , string > ;
function myFunc ( ) : Result < number , string > {
return safeTry < number , string > ( function * ( ) {
return ok (
// If the result of mayFail1().mapErr() is an `Err`, the evaluation is
// aborted here and the enclosing `safeTry` block is evaluated to that `Err`.
// Otherwise, this `(yield* ...)` is evaluated to its `.value`.
( yield * mayFail1 ( )
. mapErr ( e => `aborted by an error from 1st function, ${ e } ` ) )
+
// The same as above.
( yield * mayFail2 ( )
. mapErr ( e => `aborted by an error from 2nd function, ${ e } ` ) )
)
} )
} Чтобы использовать safeTry , точки следующие.
yield* <RESULT> для указания «return <RESULT> , если это Err , в противном случае оценить его .valuesafeTry Вы также можете использовать функцию Async Generator для передачи асинхрового блока в safeTry .
// You can use either Promise<Result> or ResultAsync.
declare function mayFail1 ( ) : Promise < Result < number , string > > ;
declare function mayFail2 ( ) : ResultAsync < number , string > ;
function myFunc ( ) : Promise < Result < number , string > > {
return safeTry < number , string > ( async function * ( ) {
return ok (
// You have to await if the expression is Promise<Result>
( yield * ( await mayFail1 ( ) )
. mapErr ( e => `aborted by an error from 1st function, ${ e } ` ) )
+
// You can call `safeUnwrap` directly if its ResultAsync
( yield * mayFail2 ( )
. mapErr ( e => `aborted by an error from 2nd function, ${ e } ` ) )
)
} )
}Для получения дополнительной информации см. #448 и #444
⬆ Вернуться к вершине
Акции Result имеют два небезопасных метода, удачно называемые _unsafeUnwrap и _unsafeUnwrapErr которые следует использовать только в тестовой среде .
_unsafeUnwrap получает Result<T, E> и возвращает T когда результат - Ok , в противном случае он бросает пользовательский объект.
_unsafeUnwrapErr получает Result<T, E> и возвращает E когда результат является Err , в противном случае он бросает пользовательский объект.
Таким образом, вы можете сделать что -то вроде:
expect ( myResult . _unsafeUnwrap ( ) ) . toBe ( someExpectation ) Тем не менее, обратите внимание, что экземпляры Result сопоставимы. Таким образом, вам не обязательно нужно развернуть их, чтобы утверждать ожидания в ваших тестах. Так что вы также можете сделать что -то вроде этого:
import { ok } from 'neverthrow'
// ...
expect ( callSomeFunctionThatReturnsAResult ( "with" , "some" , "args" ) ) . toEqual ( ok ( someExpectation ) ) ; По умолчанию значение брошенного не содержит трассировки стека. Это связано с тем, что генерация трассировки стека делает сообщения об ошибках в шутку труднее понять. Если вы хотите сгенерировать следы стека, вызовите _unsafeUnwrap и / или _unsafeUnwrapErr с объектом конфигурации:
_unsafeUnwrapErr ( {
withStackTrace : true ,
} )
// ^ Now the error object will have a `.stack` property containing the current stackЕсли вы найдете этот пакет полезным, пожалуйста, подумайте о том, чтобы спонсировать меня или просто купить мне кофе!
Хотя пакет называется neverthrow , пожалуйста, не принимайте это буквально. Я просто поощряю разработчика больше думать о эргономике и использовании любого программного обеспечения, которое они пишут.
Throw и catching очень похожи на использование операторов goto - другими словами; Это усложняет рассуждение о ваших программах. Во -вторых, используя throw вы предполагаете, что вызывающий абонент вашей функции реализует catch . Это известный источник ошибок. Пример: один Dev throw S и другой Dev использует функцию без предварительного знания, которую будет бросить функция. Таким образом, и Edge Case остался без вести, и теперь у вас есть несчастные пользователи, боссы, кошки и т. Д.
С учетом всего сказанного, определенно есть хорошие варианты использования для добавления вашей программы. Но гораздо меньше, чем вы могли бы подумать.
Проект NeverThrow доступен в качестве открытого исходного кода в соответствии с условиями лицензии MIT.