
Engieren Sie den Fehler in Ihr Programm.
Dieses Paket enthält einen Result , der entweder Erfolg ( Ok ) oder Misserfolg ( Err ) darstellt.
Bei asynchronen Aufgaben bietet neverthrow eine ResultAsync , in der ein Promise<Result<T, E>> verspricht und Ihnen das gleiche Maß an Expression und Kontrolle wie regelmäßiges Result<T, E> gibt.
ResultAsync ist thenable bedeutet, dass es sich genau wie ein natives Promise<Result> ... außer Sie haben Zugriff auf dieselben Methoden, die Result liefern, ohne await zu müssen oder das .then ! Schauen Sie sich das Wiki für Beispiele und Best Practices an.
Müssen Sie echte Beispiele dafür sehen, wie Sie dieses Paket für die Fehlerbehandlung nutzen können? Siehe dieses Repo: https://github.com/parlez-vous/server
eslint-plugin-neverthrowResult )okerrResult.isOk (Methode)Result.isErr (Methode)Result.map (Methode)Result.mapErr (Methode)Result.unwrapOr (Methode)Result.andThen (Methode)Result.asyncAndThen (Methode)Result.orElse (Methode)Result.match (Methode)Result.asyncMap (Methode)Result.andTee (Methode)Result.andThrough (Methode)Result.asyncAndThrough (Methode)Result.fromThrowable (statische Klassenmethode)Result.combine (statische Klassenmethode)Result.combineWithAllErrors (statische Klassenmethode)Result.safeUnwrap()ResultAsync )okAsyncerrAsyncResultAsync.fromThrowable (statische Klassenmethode)ResultAsync.fromPromise (statische Klassenmethode)ResultAsync.fromSafePromise (statische Klassenmethode)ResultAsync.map (Methode)ResultAsync.mapErr (Methode)ResultAsync.unwrapOr (Methode)ResultAsync.andThen (Methode)ResultAsync.orElse (Methode)ResultAsync.match (Methode)ResultAsync.andTee (Methode)ResultAsync.andThrough (Methode)ResultAsync.combine (statische Klassenmethode)ResultAsync.combineWithAllErrors (statische Klassenmethode)ResultAsync.safeUnwrap()fromThrowablefromAsyncThrowablefromPromisefromSafePromisesafeTry > npm install neverthroweslint-plugin-neverthrow Im Rahmen des neverthrow Bounty-Programms erstellte User Mdbetancourt eslint-plugin-neverthrow um sicherzustellen, dass Fehler nicht unberechtigt sind.
Installieren durch Ausführen:
> npm install eslint-plugin-neverthrow Mit eslint-plugin-neverthrow sind Sie gezwungen, das Ergebnis in einer der folgenden drei Möglichkeiten zu konsumieren:
.match.unwrapOr._unsafeUnwrap an Dies stellt sicher, dass Sie explizit mit dem Fehler Ihres Result umgehen.
Dieses Plugin ist im Wesentlichen eine Portierung des must-use Attributs von Rust.
neverthrow enthüllt Folgendes:
ok Convenience -Funktion, um eine Ok -Variante des Result zu erstellenerr eine Err -Variante des ResultOk Klasse und TypErr Klasse und TypResult sowie Namespace / Objekt, von dem er Result.fromThrowable , result.combine aufrufen können.ResultAsync KlasseokAsync Convenience -Funktion zum Erstellen eines ResultAsync mit einem Ok -Typ ResulterrAsync Sie ein ResultAsync das ein Result Err -Typs enthält import {
ok ,
Ok ,
err ,
Err ,
Result ,
okAsync ,
errAsync ,
ResultAsync ,
fromAsyncThrowable ,
fromThrowable ,
fromPromise ,
fromSafePromise ,
safeTry ,
} from 'neverthrow' Schauen Sie sich das Wiki an, um Hilfe zu erhalten, um das Beste aus neverthrow zu machen.
Wenn Sie dieses Paket nützlich finden, sollten Sie mich sponsern oder einfach einen Kaffee kaufen!
Result ) ok Konstruiert eine Ok -Variante des Result
Unterschrift:
ok < T , E > ( value : T ) : Ok < T , E > { ... }Beispiel:
import { ok } from 'neverthrow'
const myResult = ok ( { myData : 'test' } ) // instance of `Ok`
myResult . isOk ( ) // true
myResult . isErr ( ) // false⬆️ zurück nach oben
err Konstruiert eine Err -Variante des Result
Unterschrift:
err < T , E > ( error : E ) : Err < T , E > { ... }Beispiel:
import { err } from 'neverthrow'
const myResult = err ( 'Oh noooo' ) // instance of `Err`
myResult . isOk ( ) // false
myResult . isErr ( ) // true⬆️ Zurück nach oben
Result.isOk (Methode) Gibt true zurück, wenn das Ergebnis eine Ok -Variante ist
Unterschrift:
isOk ( ) : boolean { ... }⬆️ Zurück nach oben
Result.isErr (Methode) Gibt true zurück, wenn das Ergebnis eine Err -Variante ist
Unterschrift :
isErr ( ) : boolean { ... }⬆️ Zurück nach oben
Result.map (Methode) Karten Sie ein Result<T, E> , um Result<U, E> , indem eine Funktion auf einen enthaltenen Ok -Wert angewendet wird, wodurch ein Err -Wert unberührt bleibt.
Diese Funktion kann verwendet werden, um die Ergebnisse von zwei Funktionen zu komponieren.
Unterschrift:
class Result < T , E > {
map < U > ( callback : ( value : T ) => U ) : Result < U , E > { ... }
}Beispiel :
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⬆️ zurück nach oben
Result.mapErr (Methode) Karten Sie ein Result<T, E> , um Result<T, F> durch Anwenden einer Funktion auf einen enthaltenden Err -Wert anzuwenden, wodurch ein Ok -Wert unberührt bleibt.
Diese Funktion kann verwendet werden, um ein erfolgreiches Ergebnis zu durchlaufen, während Sie einen Fehler bearbeiten.
Unterschrift:
class Result < T , E > {
mapErr < F > ( callback : ( error : E ) => F ) : Result < T , F > { ... }
}Beispiel :
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⬆️ Zurück nach oben
Result.unwrapOr (Methode) Den Ok -Wert auspacken oder den Standard zurückgeben, wenn ein Err vorhanden ist
Unterschrift:
class Result < T , E > {
unwrapOr < T > ( value : T ) : T { ... }
}Beispiel :
const myResult = err ( 'Oh noooo' )
const multiply = ( value : number ) : number => value * 2
const unwrapped : number = myResult . map ( multiply ) . unwrapOr ( 10 )⬆️ Zurück nach oben
Result.andThen (Methode) Gleiche Idee wie map oben. Außer Sie müssen ein neues Result zurückgeben.
Der zurückgegebene Wert ist ein Result . Ab v4.1.0-beta können Sie unterschiedliche Fehlertypen zurückgeben (siehe Signatur unten). Vor v4.1.0-beta konnte der Fehlertyp nicht unterschiedlich sein.
Dies ist nützlich, wenn Sie mit dem inneren T -Wert eine nachfolgende Berechnung durchführen müssen, aber diese Berechnung könnte fehlschlagen.
Zusätzlich ist andThen ist als Werkzeug wirklich nützlich, um ein Result<Result<A, E2>, E1> in ein Result<A, E2> zu überfließen (siehe Beispiel unten).
Unterschrift:
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 > { ... }
}Beispiel 1: Erkettungsergebnisse
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)Beispiel 2: Abflachende verschachtelte Ergebnisse
// It's common to have nested Results
const nested = ok ( ok ( 1234 ) )
// notNested is a Ok(1234)
const notNested = nested . andThen ( ( innerResult ) => innerResult )⬆️ Zurück nach oben
Result.asyncAndThen (Methode) Gleiche Idee wie andThen oben, außer dass Sie ein neues ResultAsync zurückgeben müssen.
Der zurückgegebene Wert ist ein ResultAsync .
Unterschrift:
class Result < T , E > {
asyncAndThen < U , F > (
callback : ( value : T ) => ResultAsync < U , F >
) : ResultAsync < U , E | F > { ... }
}⬆️ Zurück nach oben
Result.orElse (Methode) Nimmt einen Err -Wert auf und ordnet es einem Result<T, SomeNewType> . Dies ist nützlich für die Fehlerwiederherstellung.
Unterschrift:
class Result < T , E > {
orElse < U , A > (
callback : ( error : E ) => Result < U , A >
) : Result < U | T , A > { ... }
}Beispiel:
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 )
)⬆️ Zurück nach oben
Result.match (Methode) Geben Sie 2 Funktionen (eine für die Ok -Variante und eine für die Err -Variante) die Funktion aus, die der Result entspricht.
Übereinstimmende Rückrufe müssen nicht ein Result zurückgeben. Sie können jedoch ein Result zurückgeben, wenn Sie möchten.
Unterschrift:
class Result < T , E > {
match < A , B = A > (
okCallback : ( value : T ) => A ,
errorCallback : ( error : E ) => B
) : A | B => { ... }
} match ist wie map und mapErr , mit der Unterscheidung, dass beide Funktionen den match Rückgabetyp haben müssen. Die Unterschiede zwischen match und map und mapErr sind:
match müssen beide Funktionen den gleichen Rückgabetyp A habenmatch wird das Result<T, E> in ein A (die Übereinstimmung der Übereinstimmung Funktionen) ausgepackt.Beispiel:
// 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` Wenn Sie den Fehlerparameter in Ihrem Match -Rückruf nicht verwenden, entspricht die match der map mit unwrapOr :
const answer = computationThatMightFail ( ) . match (
( str ) => str . toUpperCase ( ) ,
( ) => 'ComputationError'
)
// `answer` is of type `string`
const answer = computationThatMightFail ( )
. map ( ( str ) => str . toUpperCase ( ) )
. unwrapOr ( 'ComputationError' )⬆️ Zurück nach oben
Result.asyncMap (Methode) Ähnlich wie map mit Ausnahme von zwei Dingen:
Promise zurückgebenResultAsync zurück Sie können dann das Ergebnis von asyncMap unter Verwendung der ResultAsync -APIs (wie map , mapErr andThen usw.) ketten
Unterschrift:
class Result < T , E > {
asyncMap < U > (
callback : ( value : T ) => Promise < U >
) : ResultAsync < U , E > { ... }
}Beispiel:
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 ) Beachten Result .asyncMap dass im .map Beispiel .then() asyncRes parseHeaders Err Err zurückgibt await
⬆️ Zurück nach oben
Result.andTee (Methode) Nimmt ein Result<T, E> und lässt das ursprüngliche Result<T, E> unabhängig vom Ergebnis der überschrittenen Funktion durchlaufen. Dies ist eine praktische Möglichkeit, Nebenwirkungen zu bewältigen, deren Versagen oder Erfolg Ihre Hauptlogiken wie Protokollierung nicht beeinflussen sollte.
Unterschrift:
class Result < T , E > {
andTee (
callback : ( value : T ) => unknown
) : Result < T , E > { ... }
}Beispiel:
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." )
}
} ) )⬆️ zurück nach oben
Result.andThrough (Methode) Ähnlich wie bei andTee außer::
Unterschrift:
class Result < T , E > {
andThrough < F > (
callback : ( value : T ) => Result < unknown , F >
) : Result < T , E | F > { ... }
}Beispiel:
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." )
}
} ) )⬆️ zurück nach oben
Result.asyncAndThrough (Methode) Ähnlich wie bei andThrough , außer dass Sie ein Ergebnis zurückgeben müssen.
Sie können dann das Ergebnis von asyncAndThrough unter Verwendung der ResultAsync -APIs (wie map , mapErr andThen usw.) ketten
Unterschrift:
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." )
}
} ) )⬆️ Zurück nach oben
Result.fromThrowable (statische Klassenmethode)Obwohl das Ergebnis keine tatsächliche JS -Klasse ist, erfordert die Art und Weise, wie es
fromThrowableimplementiert wurde,fromThrowableals ob es sich um eine statische Methode fürResulthandelt. Siehe Beispiele unten.
Die JavaScript -Community hat sich auf die Konvention der Ausnahme der Ausnahmen geeinigt. Wenn Sie sich mit Bibliotheken von Drittanbietern verbinden, ist es unbedingt erforderlich, dass Sie Code von Drittanbietern in Try / Catch-Blöcke einwickeln.
Diese Funktion erstellt eine neue Funktion, die ein Err zurückgibt, wenn die ursprüngliche Funktion wirkt.
Es ist nicht möglich, die Arten der in der ursprünglichen Funktion geworfenen Fehler zu kennen. Daher wird empfohlen, das zweite Argument errorFn zu verwenden, um das zu kartieren, was auf einen bekannten Typ geworfen wird.
Beispiel :
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 ( "{" ) ;⬆️ Zurück nach oben
Result.combine (statische Klassenmethode)Obwohl das Ergebnis keine tatsächliche JS -Klasse ist, muss die Art und Weise, wie das
combineimplementiert wurde, implementiert, dass Siecombineso aufrufen, als wäre es eine statische Methode fürResult. Siehe Beispiele unten.
Kombinieren Sie Listen von Result s.
Wenn Sie mit Promise.all vertraut sind.
combine Werke sowohl auf heterogenen als auch auf homogenen Listen . Dies bedeutet, dass Sie Listen haben können, die verschiedene Arten von Result enthalten und sie dennoch kombinieren können. Beachten Sie, dass Sie keine Listen kombinieren können, die sowohl Result als auch ResultAsync enthalten.
Die Kombinationsfunktion enthält eine Liste von Ergebnissen und gibt ein einziges Ergebnis zurück. Wenn alle Ergebnisse in der Liste Ok sind, ist der Rückgabewert ein Ok , das eine Liste aller einzelnen Ok -Werte enthält.
Wenn nur einer der Ergebnisse in der Liste ein Err ist, gibt die Kombinationsfunktion den ERR -Wert zurück (Kurzschaltungen und das erste ERR, das sie findet).
Formell gesehen:
// 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 infinitumBeispiel:
const resultList : Result < number , never > [ ] =
[ ok ( 1 ) , ok ( 2 ) ]
const combinedList : Result < number [ ] , unknown > =
Result . combine ( resultList )Beispiel mit Tupeln:
/** @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 )⬆️ Zurück nach oben
Result.combineWithAllErrors (statische Klassenmethode)Obwohl das Ergebnis keine tatsächliche JS -Klasse ist, erfordert die Art und Weise, wie
combineWithAllErrorsimplementiert wurde,combineWithAllErrorsals ob es sich um eine statische Methode fürResulthandelt. Siehe Beispiele unten.
Wie combine , aber ohne Kurzschluss. Anstelle des ersten Fehlerwerts erhalten Sie eine Liste aller Fehlerwerte der Eingabeergebnisliste.
Wenn nur einige Ergebnisse fehlschlagen, enthält die neue kombinierte Fehlerliste nur den Fehlerwert der fehlgeschlagenen Ergebnisse, was bedeutet, dass die Länge der neuen Fehlerliste keine Garantie gibt.
Funktionssignatur:
// 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 infinitumBeispiel Verwendung:
const resultList : Result < number , string > [ ] = [
ok ( 123 ) ,
err ( 'boooom!' ) ,
ok ( 456 ) ,
err ( 'ahhhhh!' ) ,
]
const result = Result . combineWithAllErrors ( resultList )
// result is Err(['boooom!', 'ahhhhh!'])⬆️ zurück nach oben
Result.safeUnwrap()Veraltet . Sie müssen diese Methode nicht mehr anwenden.
Ermöglicht das Entpacken eines Result oder die Rückgabe eines Err implizit, wodurch die Kesselplatte reduziert wird.
⬆️ zurück nach oben
ResultAsync ) okAsync Konstruiert eine Ok -Variante von ResultAsync
Unterschrift:
okAsync < T , E > ( value : T ) : ResultAsync < T , E >Beispiel:
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⬆️ Zurück nach oben
errAsync Konstruiert eine Err -Variante von ResultAsync
Unterschrift:
errAsync < T , E > ( error : E ) : ResultAsync < T , E >Beispiel:
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⬆️ Zurück nach oben
ResultAsync.fromThrowable (statische Klassenmethode) Ähnlich wie bei Ergebnis.FromThrowable, aber für Funktionen, die ein Promise zurückgeben.
Beispiel :
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> Beachten Sie, dass dies sicherer sein kann als die Verwendung von resultasync.fromise mit dem Ergebnis eines Funktionsaufrufs, da nicht alle Funktionen, die ein Promise zurückgeben, async sind und daher Fehler synchron werfen können, anstatt ein abgelehntes Promise zurückzugeben. Zum Beispiel:
// 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' ) )⬆️ Zurück nach oben
ResultAsync.fromPromise (statische Klassenmethode) Verwandelt ein PromiseLike<T> (das darf) in ein ResultAsync<T, E> verwandelt.
Das zweite Argument behandelt den Ablehnungsfall des Versprechens und ordnet den Fehler von unknown in einen Typ E ab.
Unterschrift:
// 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 > { ... } Wenn Sie mit PromiseLike Objekten arbeiten, die Sie für eine Tatsache nicht werfen werden, verwenden Sie dann fromSafePromise , um zu vermeiden, dass ein redundantes errorHandler -Argument bestehen muss.
Beispiel :
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>⬆️ zurück nach oben
ResultAsync.fromSafePromise (statische Klassenmethode) Wie ResultAsync.fromPromise , außer dass es nicht die Ablehnung des Versprechens behandelt. Stellen Sie sicher, dass Sie wissen, was Sie tun, andernfalls wird eine ausgeworfene Ausnahme innerhalb dieses Versprechens dazu führen, dass die Ergebnisse abgelehnt werden, anstatt ein Ergebnis zu lösen.
Unterschrift:
// 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 > { ... }Beispiel :
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 ) )
} )
)⬆️ Zurück nach oben
ResultAsync.map (Methode) Ordnet ein ResultAsync<T, E> auf ResultAsync<U, E> zu, indem eine Funktion auf einen enthaltenen Ok -Wert angewendet wird, wodurch ein Err -Wert unberührt bleibt.
Die angewendete Funktion kann synchron oder asynchron sein (ein Promise<U> ) ohne Auswirkungen auf den Rückgabetyp.
Diese Funktion kann verwendet werden, um die Ergebnisse von zwei Funktionen zu komponieren.
Unterschrift:
class ResultAsync < T , E > {
map < U > (
callback : ( value : T ) => U | Promise < U >
) : ResultAsync < U , E > { ... }
}Beispiel :
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 ( ',' ) )
}
} )⬆️ Zurück nach oben
ResultAsync.mapErr (Methode) Ordnet eine ResultAsync<T, E> zu ResultAsync<T, F> durch, indem eine Funktion auf einen enthaltenden Err -Wert angewendet wird, wodurch ein Ok -Wert unberührt bleibt.
Die angewendete Funktion kann synchron oder asynchron sein (ein Promise<F> ) ohne Auswirkungen auf den Rückgabetyp.
Diese Funktion kann verwendet werden, um ein erfolgreiches Ergebnis zu durchlaufen, während Sie einen Fehler bearbeiten.
Unterschrift:
class ResultAsync < T , E > {
mapErr < F > (
callback : ( error : E ) => F | Promise < F >
) : ResultAsync < T , F > { ... }
}Beispiel :
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
} )
}
} )⬆️ Zurück nach oben
ResultAsync.unwrapOr (Methode) Den Ok -Wert auspacken oder den Standard zurückgeben, wenn ein Err vorhanden ist.
Funktioniert genau Promise<T> Result.unwrapOr T
Unterschrift:
class ResultAsync < T , E > {
unwrapOr < T > ( value : T ) : Promise < T > { ... }
}Beispiel :
const unwrapped : number = await errAsync ( 0 ) . unwrapOr ( 10 )
// unwrapped = 10⬆️ Zurück nach oben
ResultAsync.andThen (Methode) Gleiche Idee wie map oben. Mit Ausnahme der angewendeten Funktion muss ein Result oder ResultAsync zurückgeben.
ResultAsync.andThen gibt unabhängig von der Rückgabetyp der angewandten Funktion immer ein ResultAsync zurück.
Dies ist nützlich, wenn Sie mit dem inneren T -Wert eine nachfolgende Berechnung durchführen müssen, aber diese Berechnung könnte fehlschlagen.
andThen ist als Werkzeug wirklich nützlich, um ein ResultAsync<ResultAsync<A, E2>, E1> in ein ResultAsync<A, E2> zu verflachen (siehe Beispiel unten).
Unterschrift:
// 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 > { ... }
}Beispiel
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." )
}
} )⬆️ Zurück nach oben
ResultAsync.orElse (Methode) Nimmt einen Err -Wert an und ordnet ihn einem ResultAsync<T, SomeNewType> ab. Dies ist nützlich für die Fehlerwiederherstellung.
Unterschrift:
class ResultAsync < T , E > {
orElse < U , A > (
callback : ( error : E ) => Result < U , A > | ResultAsync < U , A >
) : ResultAsync < U | T , A > { ... }
}⬆️ Zurück nach oben
ResultAsync.match (Methode) Geben Sie 2 Funktionen (eine für die Ok -Variante und eine für die Err -Variante) die Funktion aus, die mit der ResultAsync Variante übereinstimmt.
Der Unterschied zum Result.match besteht darin, dass es aufgrund der asynchronen Natur des ResultAsync immer ein Promise zurückgibt.
Unterschrift:
class ResultAsync < T , E > {
match < A , B = A > (
okCallback : ( value : T ) => A ,
errorCallback : ( error : E ) => B
) : Promise < A | B > => { ... }
}Beispiel:
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⬆️ Zurück nach oben
ResultAsync.andTee (Methode) Nimmt ein ResultAsync<T, E> und lässt die ursprünglichen ResultAsync<T, E> unabhängig vom Ergebnis der überschrittenen Funktion durch. Dies ist eine praktische Möglichkeit, Nebenwirkungen zu bewältigen, deren Versagen oder Erfolg Ihre Hauptlogiken wie Protokollierung nicht beeinflussen sollte.
Unterschrift:
class ResultAsync < T , E > {
andTee (
callback : ( value : T ) => unknown
) : ResultAsync < T , E > => { ... }
}Beispiel:
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." )
}
} ) ) ⬆️ Zurück nach oben
ResultAsync.andThrough (Methode) Ähnlich wie bei andTee außer::
Unterschrift:
class ResultAsync < T , E > {
andThrough < F > (
callback : ( value : T ) => Result < unknown , F > | ResultAsync < unknown , F > ,
) : ResultAsync < T , E | F > => { ... }
}Beispiel:
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." )
}
} ) ) ⬆️ Zurück nach oben
ResultAsync.combine (statische Klassenmethode) Kombinieren Sie Listen von ResultAsync s.
Wenn Sie mit Promise.all vertraut sind.
combine Werke sowohl auf heterogenen als auch auf homogenen Listen . Dies bedeutet, dass Sie Listen haben können, die verschiedene Arten von ResultAsync enthalten und sie dennoch kombinieren können. Beachten Sie, dass Sie keine Listen kombinieren können, die sowohl Result als auch ResultAsync enthalten.
Die Kombinationsfunktion enthält eine Liste von Ergebnissen und gibt ein einziges Ergebnis zurück. Wenn alle Ergebnisse in der Liste Ok sind, ist der Rückgabewert ein Ok , das eine Liste aller einzelnen Ok -Werte enthält.
Wenn nur einer der Ergebnisse in der Liste ein Err ist, gibt die Kombinationsfunktion den ERR -Wert zurück (Kurzschaltungen und das erste ERR, das sie findet).
Formell gesehen:
// 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 infinitumBeispiel:
const resultList : ResultAsync < number , never > [ ] =
[ okAsync ( 1 ) , okAsync ( 2 ) ]
const combinedList : ResultAsync < number [ ] , unknown > =
ResultAsync . combine ( resultList )Beispiel mit Tupeln:
/** @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 )⬆️ Zurück nach oben
ResultAsync.combineWithAllErrors (statische Klassenmethode) Wie combine , aber ohne Kurzschluss. Anstelle des ersten Fehlerwerts erhalten Sie eine Liste aller Fehlerwerte der Eingabeergebnisliste.
Wenn nur einige Ergebnisse fehlschlagen, enthält die neue kombinierte Fehlerliste nur den Fehlerwert der fehlgeschlagenen Ergebnisse, was bedeutet, dass die Länge der neuen Fehlerliste keine Garantie gibt.
Funktionssignatur:
// 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 infinitumBeispiel Verwendung:
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()Veraltet . Sie müssen diese Methode nicht mehr anwenden.
Ermöglicht das Entpacken eines Result oder die Rückgabe eines Err implizit, wodurch die Kesselplatte reduziert wird.
⬆️ Zurück nach oben
fromThrowable Top -Level -Export von Result.fromThrowable . Weitere Informationen finden Sie in Ergebnis.fromothable
⬆️ zurück nach oben
fromAsyncThrowable Top -Level -Export von ResultAsync.fromThrowable . Die Dokumentation finden Sie unter resultasync.fromthrowable
⬆️ zurück nach oben
fromPromise Top -Level -Export von ResultAsync.fromPromise . Die Dokumentation finden Sie unter resultasync.frompromise
⬆️ zurück nach oben
fromSafePromise Top -Level -Export von ResultAsync.fromSafePromise . Die Dokumentation finden Sie unter resultasync.fromsafepromise
⬆️ zurück nach oben
safeTryWird verwendet, um Fehler zurückzugeben und die Kesselplatte zu reduzieren.
Nehmen wir an, wir schreiben eine Funktion, die ein Result zurückgibt, und in dieser Funktion nennen wir einige Funktionen, die auch Result zurückgeben, und wir überprüfen diese Ergebnisse, um festzustellen, ob wir weitermachen oder abbrechen sollten. Normalerweise schreiben wir wie Folgendes.
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 ) ;
} Grundsätzlich müssen wir eine Konstante für jedes Ergebnis definieren, um zu prüfen, ob es sich um ein Ok handelt, und ihren .value oder .error lesen.
Mit Safetry können wir sagen, dass wir hier zurückkehren, wenn es ein Err ist, ansonsten es hier auspacken und weitermachen. " in nur einem Ausdruck.
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 } ` ) )
)
} )
} Um safeTry zu verwenden, sind die Punkte wie folgt.
yield* <RESULT> verwenden, um 'return <RESULT> zu sagen, wenn es sich um ein Err handelt, ansonsten bewerten zu seinem .value '. 'safeTry Sie können auch eine Async -Generatorfunktion verwenden, um einen Async -Block an safeTry zu übergeben.
// 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 } ` ) )
)
} )
}Weitere Informationen finden Sie unter #448 und #444
⬆️ Zurück nach oben
Result haben zwei unsichere Methoden, die treffend als _unsafeUnwrap und _unsafeUnwrapErr bezeichnet werden und nur in einer Testumgebung verwendet werden sollten .
_unsafeUnwrap nimmt ein Result<T, E> und gibt ein T zurück, wenn das Ergebnis ein Ok ist, andernfalls wirft es ein benutzerdefiniertes Objekt aus.
_unsafeUnwrapErr nimmt ein Result<T, E> und gibt ein E zurück, wenn das Ergebnis ein Err ist, ansonsten wirft es ein benutzerdefiniertes Objekt aus.
Auf diese Weise können Sie so etwas wie:
expect ( myResult . _unsafeUnwrap ( ) ) . toBe ( someExpectation ) Beachten Sie jedoch, dass Result vergleichbar sind. Sie müssen sie also nicht unbedingt auspacken, um die Erwartungen in Ihren Tests zu gründen. Sie könnten also auch so etwas tun:
import { ok } from 'neverthrow'
// ...
expect ( callSomeFunctionThatReturnsAResult ( "with" , "some" , "args" ) ) . toEqual ( ok ( someExpectation ) ) ; Standardmäßig enthält der geworfene Wert keine Stapelspur. Dies liegt daran, dass die Stack Trace -Generierung Fehlermeldungen im Scherz schwerer zu verstehen macht. Wenn Sie möchten, dass Stapelspuren generiert werden, rufen Sie _unsafeUnwrap und / oder _unsafeUnwrapErr mit einem Konfigurationsobjekt an:
_unsafeUnwrapErr ( {
withStackTrace : true ,
} )
// ^ Now the error object will have a `.stack` property containing the current stackWenn Sie dieses Paket nützlich finden, sollten Sie mich sponsern oder einfach einen Kaffee kaufen!
Obwohl das Paket neverthrow heißt, nehmen Sie dies bitte nicht wörtlich. Ich ermutige den Entwickler einfach, ein bisschen mehr über die Ergonomie und Verwendung der Software nachzudenken, die er schreibt.
Throw und catching ist sehr ähnlich wie bei goto -Aussagen - mit anderen Worten; Es macht die Begründung über Ihre Programme schwieriger. Zweitens throw Sie die Annahme, dass der Anrufer Ihrer Funktion catch implementiert. Dies ist eine bekannte Quelle für Fehler. Beispiel: Ein Entwickler throw und ein anderes Entwickler verwendet die Funktion ohne Vorkenntnis, dass die Funktion werfen wird. So wurde der Fall von Edge unberechtigt und jetzt haben Sie unglückliche Benutzer, Chefs, Katzen usw.
Nach all dem gibt es definitiv gute Anwendungsfälle, um Ihr Programm einzugeben. Aber viel weniger als du vielleicht denkst.
Das Neverthrow -Projekt ist unter den Bedingungen der MIT -Lizenz als Open Source erhältlich.