
Encoder l'échec de votre programme.
Ce package contient un type Result qui représente le succès ( Ok ) ou l'échec ( Err ).
Pour les tâches asynchrones, neverthrow propose une classe ResultAsync qui enroule une Promise<Result<T, E>> et vous donne le même niveau d'expressivité et de contrôle qu'un Result<T, E> .
ResultAsync est thenable ce qui signifie qu'il se comporte exactement comme une Promise<Result> ... sauf que vous avez accès aux mêmes méthodes que Result fournit sans avoir à await ou .then la promesse! Consultez le wiki pour des exemples et les meilleures pratiques.
Besoin de voir des exemples réels de la façon de tirer parti de ce package pour la gestion des erreurs? Voir ce dépôt: https://github.com/parlez-vous/server
eslint-plugin-neverthrowResult )okerrResult.isOk (méthode)Result.isErr (méthode)Result.map (méthode)Result.mapErr (méthode)Result.unwrapOr (méthode)Result.andThen (méthode)Result.asyncAndThen (méthode)Result.orElse (méthode)Result.match (méthode)Result.asyncMap (méthode)Result.andTee (méthode)Result.andThrough (méthode)Result.asyncAndThrough (méthode)Result.fromThrowableResult.combine (méthode de classe statique)Result.combineWithAllErrors (méthode de classe statique)Result.safeUnwrap()ResultAsync )okAsyncerrAsyncResultAsync.fromThrowable (méthode de classe statique)ResultAsync.fromPromise (méthode de classe statique)ResultAsync.fromSafePromise (méthode de classe statique)ResultAsync.map (méthode)ResultAsync.mapErr (méthode)ResultAsync.unwrapOr (méthode)ResultAsync.andThen (méthode)ResultAsync.orElse (méthode)ResultAsync.match (méthode)ResultAsync.andTee (méthode)ResultAsync.andThrough (méthode)ResultAsync.combine (méthode de classe statique)ResultAsync.combineWithAllErrors (méthode de classe statique)ResultAsync.safeUnwrap()fromThrowablefromAsyncThrowablefromPromisefromSafePromisesafeTry > npm install neverthroweslint-plugin-neverthrow Dans le cadre du programme Bounty de neverthrow , l'utilisateur MDBETANCourt a créé eslint-plugin-neverthrow pour s'assurer que les erreurs ne sont pas passées non.
Installer en fonctionnant:
> npm install eslint-plugin-neverthrow Avec eslint-plugin-neverthrow , vous êtes obligé de consommer le résultat de l'une des trois façons suivantes:
.match.unwrapOr._unsafeUnwrap Cela garantit que vous gérez explicitement l'erreur de votre Result .
Ce plugin est essentiellement un portage de l'attribut must-use de Rust.
neverthrow expose ce qui suit:
ok Fonction de commodité pour créer une variante Ok du Resulterr Fonction Fonction pour créer une variante Err du ResultOk classe et typeErr Class and TypeResult ainsi que l'espace de noms / objet à partir duquel appeler Result.fromThrowable .ResultAsyncokAsync pour créer un ResultAsync contenant un Result OkerrAsync pour créer un ResultAsync contenant un Result de type Err import {
ok ,
Ok ,
err ,
Err ,
Result ,
okAsync ,
errAsync ,
ResultAsync ,
fromAsyncThrowable ,
fromThrowable ,
fromPromise ,
fromSafePromise ,
safeTry ,
} from 'neverthrow' Consultez le wiki pour obtenir de l'aide sur la façon de tirer le meilleur parti du neverthrow .
Si vous trouvez ce package utile, veuillez envisager de me parrainer ou simplement m'acheter un café!
Result ) ok Construit une variante Ok du Result
Signature:
ok < T , E > ( value : T ) : Ok < T , E > { ... }Exemple:
import { ok } from 'neverthrow'
const myResult = ok ( { myData : 'test' } ) // instance of `Ok`
myResult . isOk ( ) // true
myResult . isErr ( ) // false⬆️ Retour en haut
err Construit une variante Err du Result
Signature:
err < T , E > ( error : E ) : Err < T , E > { ... }Exemple:
import { err } from 'neverthrow'
const myResult = err ( 'Oh noooo' ) // instance of `Err`
myResult . isOk ( ) // false
myResult . isErr ( ) // true⬆️ Retour en haut
Result.isOk (méthode) Renvoie true si le résultat est une variante Ok
Signature:
isOk ( ) : boolean { ... }⬆️ Retour en haut
Result.isErr (méthode) Renvoie true si le résultat est une variante Err
Signature :
isErr ( ) : boolean { ... }⬆️ Retour en haut
Result.map (méthode) Mappe un Result<T, E> pour Result<U, E> en appliquant une fonction à une valeur Ok contenue, laissant une valeur Err intacte.
Cette fonction peut être utilisée pour composer les résultats de deux fonctions.
Signature:
class Result < T , E > {
map < U > ( callback : ( value : T ) => U ) : Result < U , E > { ... }
}Exemple :
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⬆️ Retour en haut
Result.mapErr (méthode) Mappe un Result<T, E> pour Result<T, F> en appliquant une fonction à une valeur Err contenue, laissant une valeur Ok intacte.
Cette fonction peut être utilisée pour passer par un résultat réussi tout en gérant une erreur.
Signature:
class Result < T , E > {
mapErr < F > ( callback : ( error : E ) => F ) : Result < T , F > { ... }
}Exemple :
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⬆️ Retour en haut
Result.unwrapOr (méthode) Déballez la valeur Ok , ou renvoyez la valeur par défaut s'il y a une Err
Signature:
class Result < T , E > {
unwrapOr < T > ( value : T ) : T { ... }
}Exemple :
const myResult = err ( 'Oh noooo' )
const multiply = ( value : number ) : number => value * 2
const unwrapped : number = myResult . map ( multiply ) . unwrapOr ( 10 )⬆️ Retour en haut
Result.andThen (méthode) Même idée que map ci-dessus. Sauf que vous devez retourner un nouveau Result .
La valeur retournée sera un Result . En ce qui concerne v4.1.0-beta , vous pouvez renvoyer des types d'erreur distincts (voir la signature ci-dessous). Avant v4.1.0-beta , le type d'erreur ne pourrait pas être distinct.
Ceci est utile lorsque vous devez faire un calcul ultérieur en utilisant la valeur T intérieure, mais ce calcul peut échouer.
De plus, andThen est vraiment utile comme outil pour aplatir un Result<Result<A, E2>, E1> dans un Result<A, E2> (voir l'exemple ci-dessous).
Signature:
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 > { ... }
}Exemple 1: Résultats de chaîne
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)Exemple 2: aplatir les résultats imbriqués
// It's common to have nested Results
const nested = ok ( ok ( 1234 ) )
// notNested is a Ok(1234)
const notNested = nested . andThen ( ( innerResult ) => innerResult )⬆️ Retour en haut
Result.asyncAndThen (méthode) Même idée qu'à andThen ci-dessus, sauf que vous devez retourner un nouveau ResultAsync .
La valeur renvoyée sera un ResultAsync .
Signature:
class Result < T , E > {
asyncAndThen < U , F > (
callback : ( value : T ) => ResultAsync < U , F >
) : ResultAsync < U , E | F > { ... }
}⬆️ Retour en haut
Result.orElse (méthode) Prend une valeur Err et le mappe à un Result<T, SomeNewType> . Ceci est utile pour la récupération des erreurs.
Signature:
class Result < T , E > {
orElse < U , A > (
callback : ( error : E ) => Result < U , A >
) : Result < U | T , A > { ... }
}Exemple:
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 )
)⬆️ Retour en haut
Result.match (méthode) Étant donné 2 fonctions (une pour la variante Ok et une pour la variante Err ) exécutez la fonction qui correspond à la variante Result .
Les rappels de correspondance ne nécessitent pas de renvoyer un Result , mais vous pouvez retourner un Result si vous le souhaitez.
Signature:
class Result < T , E > {
match < A , B = A > (
okCallback : ( value : T ) => A ,
errorCallback : ( error : E ) => B
) : A | B => { ... }
} match est comme map de chaînage et mapErr , avec la distinction qu'avec match , les deux fonctions doivent avoir le même type de retour. Les différences entre match et map de chaînage et mapErr sont que:
match , les deux fonctions doivent avoir le même type de retour Amatch se déroule le Result<T, E> dans un A (le type de retour des fonctions de correspondance)Exemple:
// 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` Si vous n'utilisez pas le paramètre d'erreur dans votre rappel de correspondance, match est équivalente à map de chaînage avec unwrapOr :
const answer = computationThatMightFail ( ) . match (
( str ) => str . toUpperCase ( ) ,
( ) => 'ComputationError'
)
// `answer` is of type `string`
const answer = computationThatMightFail ( )
. map ( ( str ) => str . toUpperCase ( ) )
. unwrapOr ( 'ComputationError' )⬆️ Retour en haut
Result.asyncMap (méthode) Semblable à map sauf pour deux choses:
PromiseResultAsync Vous pouvez ensuite enchaîner le résultat d' asyncMap en utilisant les API ResultAsync (comme map , mapErr andThen , etc.)
Signature:
class Result < T , E > {
asyncMap < U > (
callback : ( value : T ) => Promise < U >
) : ResultAsync < U , E > { ... }
}Exemple:
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 ) Notez que dans l'exemple ci-dessus si parseHeaders renvoie un Err , alors .map et .asyncMap ne seront pas invoqués, et la variable asyncRes se résoudra à un Err lorsqu'il est transformé en Result en utilisant await ou .then() .
⬆️ Retour en haut
Result.andTee (méthode) Prend un Result<T, E> et laisse passer le Result<T, E> passer quel que soit le résultat de la fonction passante. Il s'agit d'un moyen pratique de gérer les effets secondaires dont l'échec ou le succès ne devrait pas affecter vos logiques principales telles que la journalisation.
Signature:
class Result < T , E > {
andTee (
callback : ( value : T ) => unknown
) : Result < T , E > { ... }
}Exemple:
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." )
}
} ) )⬆️ Retour en haut
Result.andThrough (méthode) Similaire à andTee sauf:
Signature:
class Result < T , E > {
andThrough < F > (
callback : ( value : T ) => Result < unknown , F >
) : Result < T , E | F > { ... }
}Exemple:
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." )
}
} ) )⬆️ Retour en haut
Result.asyncAndThrough (méthode) Semblable à andThrough sauf, vous devez retourner un résultat.
Vous pouvez ensuite enchaîner le résultat de asyncAndThrough à l'aide des API ResultAsync (comme map , mapErr andThen , etc.)
Signature:
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." )
}
} ) )⬆️ Retour en haut
Result.fromThrowableBien que le résultat ne soit pas une classe JS réelle, la façon dont de la
fromThrowablea été implémenté nécessite que vous appeliezfromThrowableque c'était une méthode statique surResult. Voir des exemples ci-dessous.
La communauté JavaScript a convenu de la convention de lancer des exceptions. En tant que tel, lors de l'interfaçage avec des bibliothèques tierces, il est impératif que vous encluez le code tiers dans les blocs Try / Catch.
Cette fonction créera une nouvelle fonction qui renvoie un Err lorsque la fonction d'origine lancera.
Il n'est pas possible de connaître les types d'erreurs lancées dans la fonction d'origine, il est donc recommandé d'utiliser le deuxième argument errorFn pour cartographier ce qui est jeté à un type connu.
Exemple :
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 ( "{" ) ;⬆️ Retour en haut
Result.combine (méthode de classe statique)Bien que le résultat ne soit pas une classe JS réelle, la façon dont
combinea été mise en œuvre nécessite que vous appeliezcombinecomme s'il s'agissait d'une méthode statique surResult. Voir des exemples ci-dessous.
Combinez des listes de Result s.
Si vous êtes familier avec Promise.all tout, la fonction combinée fonctionne conceptuellement la même chose.
combine des travaux sur des listes hétérogènes et homogènes . Cela signifie que vous pouvez avoir des listes contenant différents types de Result tout en les combiner. Notez que vous ne pouvez pas combiner des listes contenant à la fois Result et ResultAsync .
La fonction combinée prend une liste de résultats et renvoie un seul résultat. Si tous les résultats de la liste sont Ok , la valeur de retour sera une Ok contenant une liste de toutes les valeurs Ok individuelles.
Si un seul des résultats de la liste est un Err , la fonction combinée renvoie cette valeur ERR (il court-circuit et renvoie le premier ERR qu'il trouve).
Officiellement parlant:
// 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 infinitumExemple:
const resultList : Result < number , never > [ ] =
[ ok ( 1 ) , ok ( 2 ) ]
const combinedList : Result < number [ ] , unknown > =
Result . combine ( resultList )Exemple avec des tuples:
/** @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 )⬆️ Retour en haut
Result.combineWithAllErrors (méthode de classe statique)Bien que le résultat ne soit pas une classe JS réelle, la façon dont
combineWithAllErrorsa été implémentée nécessite que vous appeliezcombineWithAllErrorscomme s'il s'agissait d'une méthode statique surResult. Voir des exemples ci-dessous.
Comme combine mais sans court-circuit. Au lieu de la première valeur d'erreur, vous obtenez une liste de toutes les valeurs d'erreur de la liste des résultats d'entrée.
Si seuls certains résultats échouent, la nouvelle liste d'erreurs combinées ne contiendra que la valeur d'erreur des résultats échoués, ce qui signifie qu'il n'y a aucune garantie de la durée de la nouvelle liste d'erreurs.
Signature de la fonction:
// 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 infinitumExemple d'utilisation:
const resultList : Result < number , string > [ ] = [
ok ( 123 ) ,
err ( 'boooom!' ) ,
ok ( 456 ) ,
err ( 'ahhhhh!' ) ,
]
const result = Result . combineWithAllErrors ( resultList )
// result is Err(['boooom!', 'ahhhhh!'])⬆️ Retour en haut
Result.safeUnwrap()Déprécié . Vous n'avez plus besoin d'utiliser cette méthode.
Permet de déballer un Result ou de renvoyer implicitement un Err , réduisant ainsi la buissier debout.
⬆️ Retour en haut
ResultAsync ) okAsync Construit une variante Ok de ResultAsync
Signature:
okAsync < T , E > ( value : T ) : ResultAsync < T , E >Exemple:
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⬆️ Retour en haut
errAsync Construit une variante Err de ResultAsync
Signature:
errAsync < T , E > ( error : E ) : ResultAsync < T , E >Exemple:
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⬆️ Retour en haut
ResultAsync.fromThrowable (méthode de classe statique) Similaire au résultat Promise
Exemple :
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> Notez que cela peut être plus sûr que d'utiliser ResultAsync.fromPromiser avec le résultat d'un appel de fonction, car toutes les fonctions qui renvoient une Promise sont async , et donc ils peuvent lancer des erreurs de manière synchrone plutôt que de renvoyer une Promise rejetée. Par exemple:
// 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' ) )⬆️ Retour en haut
ResultAsync.fromPromise (méthode de classe statique) Transforme un PromiseLike<T> (qui peut lancer) en ResultAsync<T, E> .
Le deuxième argument gère le cas de rejet de la promesse et mappe l'erreur de unknown dans un certain type E
Signature:
// 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 > { ... } Si vous travaillez avec des objets PromiseLike que vous connaissez pour un fait ne lancera pas, utilisez fromSafePromise pour éviter d'avoir à passer un argument errorHandler redondant.
Exemple :
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>⬆️ Retour en haut
ResultAsync.fromSafePromise (méthode de classe statique) Identique à ResultAsync.fromPromise sauf qu'il ne gère pas le rejet de la promesse. Assurez-vous de savoir ce que vous faites, sinon une exception lancée dans cette promesse fera rejeter les résultats de ResultAsync, au lieu de résoudre à un résultat.
Signature:
// 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 > { ... }Exemple :
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 ) )
} )
)⬆️ Retour en haut
ResultAsync.map (méthode) Mappe un ResultAsync<T, E> à ResultAsync<U, E> en appliquant une fonction à une valeur Ok contenue, laissant une valeur Err intacte.
La fonction appliquée peut être synchrone ou asynchrone (renvoyer une Promise<U> ) sans impact sur le type de retour.
Cette fonction peut être utilisée pour composer les résultats de deux fonctions.
Signature:
class ResultAsync < T , E > {
map < U > (
callback : ( value : T ) => U | Promise < U >
) : ResultAsync < U , E > { ... }
}Exemple :
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 ( ',' ) )
}
} )⬆️ Retour en haut
ResultAsync.mapErr (méthode) Mappe un ResultAsync<T, E> à résulter ResultAsync<T, F> en appliquant une fonction à une valeur Err contenue, laissant une valeur Ok intacte.
La fonction appliquée peut être synchrone ou asynchrone (retour d'une Promise<F> ) sans impact sur le type de retour.
Cette fonction peut être utilisée pour passer par un résultat réussi tout en gérant une erreur.
Signature:
class ResultAsync < T , E > {
mapErr < F > (
callback : ( error : E ) => F | Promise < F >
) : ResultAsync < T , F > { ... }
}Exemple :
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
} )
}
} )⬆️ Retour en haut
ResultAsync.unwrapOr (méthode) Déballez la valeur Ok , ou renvoyez la valeur par défaut s'il y a une Err .
Fonctionne comme Result.unwrapOr mais renvoie une Promise<T> au lieu de T .
Signature:
class ResultAsync < T , E > {
unwrapOr < T > ( value : T ) : Promise < T > { ... }
}Exemple :
const unwrapped : number = await errAsync ( 0 ) . unwrapOr ( 10 )
// unwrapped = 10⬆️ Retour en haut
ResultAsync.andThen (méthode) Même idée que map ci-dessus. Sauf que la fonction appliquée doit renvoyer un Result ou ResultAsync .
ResultAsync.andThen renvoie toujours un ResultAsync , quel que soit le type de retour de la fonction appliquée.
Ceci est utile lorsque vous devez faire un calcul ultérieur en utilisant la valeur T intérieure, mais ce calcul peut échouer.
andThen est vraiment utile comme un outil pour aplatir un ResultAsync<ResultAsync<A, E2>, E1> dans un ResultAsync<A, E2> (voir l'exemple ci-dessous).
Signature:
// 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 > { ... }
}Exemple
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." )
}
} )⬆️ Retour en haut
ResultAsync.orElse (méthode) Prend une valeur Err et le mappe à un ResultAsync<T, SomeNewType> . Ceci est utile pour la récupération des erreurs.
Signature:
class ResultAsync < T , E > {
orElse < U , A > (
callback : ( error : E ) => Result < U , A > | ResultAsync < U , A >
) : ResultAsync < U | T , A > { ... }
}⬆️ Retour en haut
ResultAsync.match (méthode) Étant donné 2 fonctions (une pour la variante Ok et une pour la variante Err ) exécutez la fonction qui correspond à la variante ResultAsync .
La différence Promise ResultAsync Result.match .
Signature:
class ResultAsync < T , E > {
match < A , B = A > (
okCallback : ( value : T ) => A ,
errorCallback : ( error : E ) => B
) : Promise < A | B > => { ... }
}Exemple:
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⬆️ Retour en haut
ResultAsync.andTee (méthode) Prend une ResultAsync<T, E> et laisse passer le ResultAsync<T, E> passer quel que soit le résultat de la fonction de passage. Il s'agit d'un moyen pratique de gérer les effets secondaires dont l'échec ou le succès ne devrait pas affecter vos logiques principales telles que la journalisation.
Signature:
class ResultAsync < T , E > {
andTee (
callback : ( value : T ) => unknown
) : ResultAsync < T , E > => { ... }
}Exemple:
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." )
}
} ) ) ⬆️ Retour en haut
ResultAsync.andThrough (méthode) Similaire à andTee sauf:
Signature:
class ResultAsync < T , E > {
andThrough < F > (
callback : ( value : T ) => Result < unknown , F > | ResultAsync < unknown , F > ,
) : ResultAsync < T , E | F > => { ... }
}Exemple:
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." )
}
} ) ) ⬆️ Retour en haut
ResultAsync.combine (méthode de classe statique) Combinez des listes de ResultAsync s.
Si vous êtes familier avec Promise.all tout, la fonction combinée fonctionne conceptuellement la même chose.
combine des travaux sur des listes hétérogènes et homogènes . Cela signifie que vous pouvez avoir des listes qui contiennent différents types de ResultAsync tout en les combiner. Notez que vous ne pouvez pas combiner des listes contenant à la fois Result et ResultAsync .
La fonction combinée prend une liste de résultats et renvoie un seul résultat. Si tous les résultats de la liste sont Ok , la valeur de retour sera une Ok contenant une liste de toutes les valeurs Ok individuelles.
Si un seul des résultats de la liste est un Err , la fonction combinée renvoie cette valeur ERR (il court-circuit et renvoie le premier ERR qu'il trouve).
Officiellement parlant:
// 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 infinitumExemple:
const resultList : ResultAsync < number , never > [ ] =
[ okAsync ( 1 ) , okAsync ( 2 ) ]
const combinedList : ResultAsync < number [ ] , unknown > =
ResultAsync . combine ( resultList )Exemple avec des tuples:
/** @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 )⬆️ Retour en haut
ResultAsync.combineWithAllErrors (méthode de classe statique) Comme combine mais sans court-circuit. Au lieu de la première valeur d'erreur, vous obtenez une liste de toutes les valeurs d'erreur de la liste des résultats d'entrée.
Si seuls certains résultats échouent, la nouvelle liste d'erreurs combinées ne contiendra que la valeur d'erreur des résultats échoués, ce qui signifie qu'il n'y a aucune garantie de la durée de la nouvelle liste d'erreurs.
Signature de la fonction:
// 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 infinitumExemple d'utilisation:
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()Déprécié . Vous n'avez plus besoin d'utiliser cette méthode.
Permet de déballer un Result ou de renvoyer implicitement un Err , réduisant ainsi la buissier debout.
⬆️ Retour en haut
fromThrowable Exportation de niveau supérieur du Result.fromThrowable . Veuillez trouver des documents au résultat.
⬆️ Retour en haut
fromAsyncThrowable Exportation de niveau supérieur de ResultAsync.fromThrowable . Veuillez trouver la documentation sur ResultAsync.fromthrowable
⬆️ Retour en haut
fromPromise Exportation de niveau supérieur de ResultAsync.fromPromise . Veuillez trouver la documentation sur ResultAsync.
⬆️ Retour en haut
fromSafePromise Exportation de niveau supérieur de ResultAsync.fromSafePromise . Veuillez trouver la documentation sur ResultAsync.fromsafepromise
⬆️ Retour en haut
safeTryUtilisé pour renvoyer implicitement les erreurs et réduire le chauffeur.
Disons que nous écrivons une fonction qui renvoie un Result , et dans cette fonction, nous appelons certaines fonctions qui renvoient également Result et nous vérifions ces résultats pour voir si nous devons continuer ou abandonner. Habituellement, nous écrivons comme les suivants.
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 ) ;
} Fondamentalement, nous devons définir une constante pour chaque résultat pour vérifier s'il s'agit d'un Ok et de lire sa .value ou .error .
Avec Safetry, nous pouvons indiquer «Retour ici si c'est un Err , sinon le compliquez ici et continue». Dans une seule expression.
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 } ` ) )
)
} )
} Pour utiliser safeTry , les points sont les suivants.
yield* <RESULT> pour indiquer «Retour <RESULT> s'il s'agit d'une Err , sinon évaluer à sa .value »safeTry Vous pouvez également utiliser la fonction du générateur asynchrone pour passer un bloc asynchronisé à 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 } ` ) )
)
} )
}Pour plus d'informations, voir # 448 et # 444
⬆️ Retour en haut
Les instances Result ont deux méthodes dangereuses, bien appelées _unsafeUnwrap et _unsafeUnwrapErr qui ne devraient être utilisées que dans un environnement de test .
_unsafeUnwrap prend un Result<T, E> et renvoie un T lorsque le résultat est Ok , sinon il lance un objet personnalisé.
_unsafeUnwrapErr prend un Result<T, E> et renvoie un E lorsque le résultat est une Err , sinon il lance un objet personnalisé.
De cette façon, vous pouvez faire quelque chose comme:
expect ( myResult . _unsafeUnwrap ( ) ) . toBe ( someExpectation ) Cependant, notez que les instances Result sont comparables. Vous n'avez donc pas nécessairement besoin de les déballer afin d'affirmer les attentes de vos tests. Vous pouvez donc aussi faire quelque chose comme ceci:
import { ok } from 'neverthrow'
// ...
expect ( callSomeFunctionThatReturnsAResult ( "with" , "some" , "args" ) ) . toEqual ( ok ( someExpectation ) ) ; Par défaut, la valeur lancée ne contient pas de trace de pile. En effet, la génération de trace de pile rend les messages d'erreur dans une plaisanterie plus difficile à comprendre. Si vous souhaitez que des traces de pile soient générées, appelez _unsafeUnwrap et / ou _unsafeUnwrapErr avec un objet de configuration:
_unsafeUnwrapErr ( {
withStackTrace : true ,
} )
// ^ Now the error object will have a `.stack` property containing the current stackSi vous trouvez ce package utile, veuillez envisager de me parrainer ou simplement m'acheter un café!
Bien que le forfait s'appelle neverthrow , veuillez ne pas le prendre littéralement. J'encourage simplement le développeur à réfléchir un peu plus à l'ergonomie et à l'utilisation de tout logiciel qu'ils écrivent.
Throw et catching sont très similaires à l'utilisation de déclarations goto - en d'autres termes; Cela rend le raisonnement sur vos programmes plus difficile. Deuxièmement, en utilisant throw , vous supposez que l'appelant de votre fonction implémente catch . Il s'agit d'une source d'erreurs connue. Exemple: Un Dev throw S et un autre Dev utilise la fonction sans savoir préalable que la fonction va lancer. Ainsi, et Edge Case a été laissé non géré et maintenant vous avez des utilisateurs malheureux, des boss, des chats, etc.
Cela dit, il y a certainement de bons cas d'utilisation pour lancer votre programme. Mais beaucoup moins que vous ne le pensez.
Le projet Neverthrow est disponible en open source en vertu des termes de la licence MIT.