
Menyandikan kegagalan ke dalam program Anda.
Paket ini berisi jenis Result yang mewakili keberhasilan ( Ok ) atau kegagalan ( Err ).
Untuk tugas -tugas asinkron, neverthrow menawarkan kelas ResultAsync yang membungkus Promise<Result<T, E>> dan memberi Anda tingkat ekspresif dan kontrol yang sama sebagai Result<T, E> .
ResultAsync adalah thenable berarti itu berperilaku persis seperti Promise<Result> ... kecuali Anda memiliki akses ke metode yang sama yang disediakan oleh Result tanpa harus await atau .then Dahulu janji! Lihat wiki untuk contoh dan praktik terbaik.
Perlu melihat contoh kehidupan nyata tentang cara memanfaatkan paket ini untuk penanganan kesalahan? Lihat repo ini: https://github.com/parlez-vous/server
eslint-plugin-neverthrowResult )okerrResult.isOk (metode)Result.isErr (metode)Result.map (metode)Result.mapErr (metode)Result.unwrapOr (metode)Result.andThen (metode)Result.asyncAndThen (metode)Result.orElse (metode)Result.match (metode)Result.asyncMap (metode)Result.andTee (metode)Result.andThrough (metode)Result.asyncAndThrough (metode)Result.fromThrowableResult.combine (Metode Kelas Statis)Result.combineWithAllErrors (Metode Kelas Statis)Result.safeUnwrap()ResultAsync )okAsyncerrAsyncResultAsync.fromThrowable (metode kelas statis)ResultAsync.fromPromise (metode kelas statis)ResultAsync.fromSafePromise (metode kelas statis)ResultAsync.map (metode)ResultAsync.mapErr (metode)ResultAsync.unwrapOr (metode)ResultAsync.andThen (metode)ResultAsync.orElse (metode)ResultAsync.match (metode)ResultAsync.andTee (metode)ResultAsync.andThrough (metode)ResultAsync.combine (metode kelas statis)ResultAsync.combineWithAllErrors (metode kelas statis)ResultAsync.safeUnwrap()fromThrowablefromAsyncThrowablefromPromisefromSafePromisesafeTry > npm install neverthroweslint-plugin-neverthrow Sebagai bagian dari program Bounty neverthrow S, pengguna mdbetancourt membuat eslint-plugin-neverthrow untuk memastikan bahwa kesalahan tidak hilang.
Instal dengan menjalankan:
> npm install eslint-plugin-neverthrow Dengan eslint-plugin-neverthrow , Anda dipaksa untuk mengkonsumsi hasilnya dalam salah satu dari tiga cara berikut:
.match.unwrapOr._unsafeUnwrap Ini memastikan bahwa Anda secara eksplisit menangani kesalahan Result Anda.
Plugin ini pada dasarnya merupakan porting atribut must-use Rust.
neverthrow memperlihatkan hal -hal berikut:
ok Fungsi kenyamanan untuk membuat varian Ok Resulterr untuk membuat varian Err ResultOk kelas dan ketikErr dan ketikResult serta namespace / objek dari mana untuk memanggil Result.fromThrowable .ResultAsyncokAsync untuk membuat ResultAsync yang berisi Result tipe OkerrAsync untuk membuat ResultAsync yang berisi Result tipe Err import {
ok ,
Ok ,
err ,
Err ,
Result ,
okAsync ,
errAsync ,
ResultAsync ,
fromAsyncThrowable ,
fromThrowable ,
fromPromise ,
fromSafePromise ,
safeTry ,
} from 'neverthrow' Lihatlah wiki untuk bantuan tentang cara memanfaatkan neverthrow .
Jika Anda menemukan paket ini berguna, silakan pertimbangkan untuk mensponsori saya atau hanya membelikan saya kopi!
Result ) ok Membangun varian Result Ok
Tanda tangan:
ok < T , E > ( value : T ) : Ok < T , E > { ... }Contoh:
import { ok } from 'neverthrow'
const myResult = ok ( { myData : 'test' } ) // instance of `Ok`
myResult . isOk ( ) // true
myResult . isErr ( ) // false⬆️ kembali ke atas
err Membangun Err Result
Tanda tangan:
err < T , E > ( error : E ) : Err < T , E > { ... }Contoh:
import { err } from 'neverthrow'
const myResult = err ( 'Oh noooo' ) // instance of `Err`
myResult . isOk ( ) // false
myResult . isErr ( ) // true⬆️ kembali ke atas
Result.isOk (metode) Mengembalikan true jika hasilnya adalah varian Ok
Tanda tangan:
isOk ( ) : boolean { ... }⬆️ kembali ke atas
Result.isErr (metode) Mengembalikan true jika hasilnya adalah varian Err
Tanda tangan :
isErr ( ) : boolean { ... }⬆️ kembali ke atas
Result.map (metode) Memetakan Result<T, E> untuk Result<U, E> dengan menerapkan fungsi ke nilai Ok yang terkandung, meninggalkan nilai Err yang tidak tersentuh.
Fungsi ini dapat digunakan untuk menyusun hasil dua fungsi.
Tanda tangan:
class Result < T , E > {
map < U > ( callback : ( value : T ) => U ) : Result < U , E > { ... }
}Contoh :
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⬆️ kembali ke atas
Result.mapErr (metode) Memetakan Result<T, E> untuk Result<T, F> dengan menerapkan fungsi ke nilai Err yang terkandung, meninggalkan nilai Ok yang tidak tersentuh.
Fungsi ini dapat digunakan untuk melewati hasil yang berhasil sambil menangani kesalahan.
Tanda tangan:
class Result < T , E > {
mapErr < F > ( callback : ( error : E ) => F ) : Result < T , F > { ... }
}Contoh :
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⬆️ kembali ke atas
Result.unwrapOr (metode) Buka Nilai Ok , atau kembalikan default jika ada Err
Tanda tangan:
class Result < T , E > {
unwrapOr < T > ( value : T ) : T { ... }
}Contoh :
const myResult = err ( 'Oh noooo' )
const multiply = ( value : number ) : number => value * 2
const unwrapped : number = myResult . map ( multiply ) . unwrapOr ( 10 )⬆️ kembali ke atas
Result.andThen (metode) Ide yang sama seperti map di atas. Kecuali Anda harus mengembalikan Result baru.
Nilai yang dikembalikan akan menjadi Result . Pada v4.1.0-beta , Anda dapat mengembalikan tipe kesalahan yang berbeda (lihat tanda tangan di bawah). Sebelum v4.1.0-beta , tipe kesalahan tidak bisa berbeda.
Ini berguna untuk ketika Anda perlu melakukan perhitungan selanjutnya menggunakan nilai T dalam, tetapi perhitungan itu mungkin gagal.
Selain itu, andThen sangat berguna sebagai alat untuk meratakan Result<Result<A, E2>, E1> ke dalam Result<A, E2> (lihat contoh di bawah).
Tanda tangan:
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 > { ... }
}Contoh 1: Hasil Rantai
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)Contoh 2: Hasil perataan bersarang
// It's common to have nested Results
const nested = ok ( ok ( 1234 ) )
// notNested is a Ok(1234)
const notNested = nested . andThen ( ( innerResult ) => innerResult )⬆️ kembali ke atas
Result.asyncAndThen (metode) Ide yang sama seperti andThen di atas, kecuali Anda harus mengembalikan ResultAsync baru.
Nilai yang dikembalikan akan menjadi ResultAsync .
Tanda tangan:
class Result < T , E > {
asyncAndThen < U , F > (
callback : ( value : T ) => ResultAsync < U , F >
) : ResultAsync < U , E | F > { ... }
}⬆️ kembali ke atas
Result.orElse (metode) Mengambil nilai Err dan memetakannya ke Result<T, SomeNewType> . Ini berguna untuk pemulihan kesalahan.
Tanda tangan:
class Result < T , E > {
orElse < U , A > (
callback : ( error : E ) => Result < U , A >
) : Result < U | T , A > { ... }
}Contoh:
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 )
)⬆️ kembali ke atas
Result.match (metode) Diberikan 2 fungsi (satu untuk varian Ok dan satu untuk varian Err ) menjalankan fungsi yang cocok dengan varian Result .
Cocokkan panggilan balik tidak mengharuskan untuk mengembalikan Result , namun Anda dapat mengembalikan Result jika Anda mau.
Tanda tangan:
class Result < T , E > {
match < A , B = A > (
okCallback : ( value : T ) => A ,
errorCallback : ( error : E ) => B
) : A | B => { ... }
} match seperti rantai map dan mapErr , dengan perbedaan yang dengan match kedua fungsi harus memiliki jenis pengembalian yang sama. Perbedaan antara match dan rantai map dan mapErr adalah itu:
match kedua fungsi harus memiliki tipe pengembalian yang Amatch Buka BROUS Result<T, E> ke A (Jenis Pengembalian Fungsi Pertandingan)Contoh:
// 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` Jika Anda tidak menggunakan parameter kesalahan dalam panggilan balik pertandingan Anda maka match setara dengan merantai map dengan unwrapOr :
const answer = computationThatMightFail ( ) . match (
( str ) => str . toUpperCase ( ) ,
( ) => 'ComputationError'
)
// `answer` is of type `string`
const answer = computationThatMightFail ( )
. map ( ( str ) => str . toUpperCase ( ) )
. unwrapOr ( 'ComputationError' )⬆️ kembali ke atas
Result.asyncMap (metode) Mirip dengan map kecuali dua hal:
PromiseResultAsync Anda kemudian dapat rantai hasil asyncMap menggunakan API ResultAsync (seperti map , mapErr , andThen , dll.)
Tanda tangan:
class Result < T , E > {
asyncMap < U > (
callback : ( value : T ) => Promise < U >
) : ResultAsync < U , E > { ... }
}Contoh:
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 ) Perhatikan bahwa dalam contoh di atas jika parseHeaders mengembalikan Err maka .map dan .asyncMap tidak akan dipanggil, dan variabel asyncRes akan diselesaikan ke Err ketika diubah menjadi Result menggunakan await atau .then() .
⬆️ kembali ke atas
Result.andTee (metode) Mengambil Result<T, E> dan memungkinkan Result<T, E> melewati terlepas dari hasil fungsi lulus. Ini adalah cara praktis untuk menangani efek samping yang kegagalan atau keberhasilannya tidak boleh memengaruhi logika utama Anda seperti logging.
Tanda tangan:
class Result < T , E > {
andTee (
callback : ( value : T ) => unknown
) : Result < T , E > { ... }
}Contoh:
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." )
}
} ) )⬆️ kembali ke atas
Result.andThrough (metode) Mirip dengan andTee kecuali untuk:
Tanda tangan:
class Result < T , E > {
andThrough < F > (
callback : ( value : T ) => Result < unknown , F >
) : Result < T , E | F > { ... }
}Contoh:
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." )
}
} ) )⬆️ kembali ke atas
Result.asyncAndThrough (metode) Mirip dengan andThrough kecuali Anda harus mengembalikan hasil yang dihasilkan.
Anda kemudian dapat rantai hasil asyncAndThrough menggunakan API ResultAsync (seperti map , mapErr , andThen , dll.)
Tanda tangan:
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." )
}
} ) )⬆️ kembali ke atas
Result.fromThrowableMeskipun hasilnya bukan kelas JS yang sebenarnya, cara yang
fromThrowablediimplementasikan mengharuskan Anda meneleponfromThrowableseolah -olah itu adalah metode statis yang ada padaResult. Lihat contoh di bawah ini.
Komunitas Javascript telah menyetujui konvensi melempar pengecualian. Dengan demikian, ketika berinteraksi dengan perpustakaan pihak ketiga, sangat penting bagi Anda untuk membungkus kode pihak ketiga dalam blok mencoba / menangkap.
Fungsi ini akan membuat fungsi baru yang mengembalikan Err saat fungsi asli melempar.
Tidak mungkin untuk mengetahui jenis kesalahan yang dilemparkan dalam fungsi asli, oleh karena itu disarankan untuk menggunakan argumen kedua errorFn untuk memetakan apa yang dilemparkan ke jenis yang diketahui.
Contoh :
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 ( "{" ) ;⬆️ kembali ke atas
Result.combine (Metode Kelas Statis)Meskipun hasilnya bukan kelas JS yang sebenarnya, cara
combinetelah diimplementasikan mengharuskan Anda memanggilcombineseolah -olah itu adalah metode statis padaResult. Lihat contoh di bawah ini.
Menggabungkan daftar Result s.
Jika Anda terbiasa dengan Promise.all , fungsi gabungan bekerja secara konseptual sama.
combine karya pada daftar yang heterogen dan homogen . Ini berarti bahwa Anda dapat memiliki daftar yang berisi berbagai jenis Result dan masih dapat menggabungkannya. Perhatikan bahwa Anda tidak dapat menggabungkan daftar yang berisi Result s dan ResultAsync s.
Fungsi gabungan mengambil daftar hasil dan mengembalikan hasil tunggal. Jika semua hasil dalam daftar Ok , maka nilai pengembalian akan menjadi Ok yang berisi daftar semua nilai Ok individu.
Jika hanya salah satu hasil dalam daftar adalah Err maka fungsi gabungan mengembalikan nilai err itu (itu sirkuit pendek dan mengembalikan kesalahan pertama yang ditemukannya).
Secara formal:
// 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 infinitumContoh:
const resultList : Result < number , never > [ ] =
[ ok ( 1 ) , ok ( 2 ) ]
const combinedList : Result < number [ ] , unknown > =
Result . combine ( resultList )Contoh dengan tupel:
/** @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 )⬆️ kembali ke atas
Result.combineWithAllErrors (Metode Kelas Statis)Meskipun hasilnya bukan kelas JS yang sebenarnya, cara
combineWithAllErrorstelah diimplementasikan mengharuskan Anda memanggilcombineWithAllErrorsseolah -olah itu adalah metode statis padaResult. Lihat contoh di bawah ini.
Seperti combine tetapi tanpa hubungan pendek. Alih -alih hanya nilai kesalahan pertama, Anda mendapatkan daftar semua nilai kesalahan dari daftar hasil input.
Jika hanya beberapa hasil yang gagal, daftar kesalahan gabungan baru hanya akan berisi nilai kesalahan dari hasil yang gagal, yang berarti bahwa tidak ada jaminan panjang daftar kesalahan baru.
Tanda Tangan Fungsi:
// 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 infinitumContoh Penggunaan:
const resultList : Result < number , string > [ ] = [
ok ( 123 ) ,
err ( 'boooom!' ) ,
ok ( 456 ) ,
err ( 'ahhhhh!' ) ,
]
const result = Result . combineWithAllErrors ( resultList )
// result is Err(['boooom!', 'ahhhhh!'])⬆️ kembali ke atas
Result.safeUnwrap()Tercerahkan . Anda tidak perlu menggunakan metode ini lagi.
Memungkinkan untuk membuka Result atau mengembalikan Err secara implisit, sehingga mengurangi boilerplate.
⬆️ kembali ke atas
ResultAsync ) okAsync Membangun varian Ok dari ResultAsync
Tanda tangan:
okAsync < T , E > ( value : T ) : ResultAsync < T , E >Contoh:
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⬆️ kembali ke atas
errAsync Membangun varian Err dari ResultAsync
Tanda tangan:
errAsync < T , E > ( error : E ) : ResultAsync < T , E >Contoh:
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⬆️ kembali ke atas
ResultAsync.fromThrowable (metode kelas statis) Mirip dengan hasil Promise
Contoh :
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> Perhatikan bahwa ini bisa lebih aman daripada menggunakan resultAsync.FromPromise dengan hasil panggilan fungsi, karena tidak semua fungsi yang mengembalikan Promise adalah async , dan dengan demikian mereka dapat melempar kesalahan secara serempak daripada mengembalikan Promise yang ditolak. Misalnya:
// 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' ) )⬆️ kembali ke atas
ResultAsync.fromPromise (metode kelas statis) Mengubah PromiseLike<T> (yang mungkin melempar) menjadi ResultAsync<T, E> .
Argumen kedua menangani kasus penolakan janji dan memetakan kesalahan dari unknown menjadi beberapa tipe E .
Tanda tangan:
// 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 > { ... } Jika Anda bekerja dengan benda -benda PromiseLike yang Anda ketahui pasti tidak akan dilemparkan, maka gunakan fromSafePromise untuk menghindari harus mengeluarkan argumen errorHandler yang berlebihan.
Contoh :
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>⬆️ kembali ke atas
ResultAsync.fromSafePromise (metode kelas statis) Sama seperti ResultAsync.fromPromise kecuali bahwa itu tidak menangani penolakan janji. Pastikan Anda tahu apa yang Anda lakukan, jika tidak, pengecualian yang dilemparkan dalam janji ini akan menyebabkan hasil yang ditolak, alih -alih menyelesaikan hasilnya.
Tanda tangan:
// 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 > { ... }Contoh :
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 ) )
} )
)⬆️ kembali ke atas
ResultAsync.map (metode) Memetakan ResultAsync<T, E> ke ResultAsync<U, E> dengan menerapkan fungsi ke nilai Ok yang terkandung, meninggalkan nilai Err yang tidak tersentuh.
Fungsi yang diterapkan dapat sinkron atau asinkron (mengembalikan Promise<U> ) tanpa dampak pada jenis pengembalian.
Fungsi ini dapat digunakan untuk menyusun hasil dua fungsi.
Tanda tangan:
class ResultAsync < T , E > {
map < U > (
callback : ( value : T ) => U | Promise < U >
) : ResultAsync < U , E > { ... }
}Contoh :
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 ( ',' ) )
}
} )⬆️ kembali ke atas
ResultAsync.mapErr (metode) Memetakan ResultAsync<T, E> ke ResultAsync<T, F> dengan menerapkan fungsi ke nilai Err yang terkandung, meninggalkan nilai Ok yang tidak tersentuh.
Fungsi yang diterapkan dapat sinkron atau asinkron (mengembalikan Promise<F> ) tanpa dampak pada tipe pengembalian.
Fungsi ini dapat digunakan untuk melewati hasil yang berhasil sambil menangani kesalahan.
Tanda tangan:
class ResultAsync < T , E > {
mapErr < F > (
callback : ( error : E ) => F | Promise < F >
) : ResultAsync < T , F > { ... }
}Contoh :
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
} )
}
} )⬆️ kembali ke atas
ResultAsync.unwrapOr (metode) Buang nilai Ok , atau kembalikan default jika ada Err .
Bekerja Promise<T> Result.unwrapOr T
Tanda tangan:
class ResultAsync < T , E > {
unwrapOr < T > ( value : T ) : Promise < T > { ... }
}Contoh :
const unwrapped : number = await errAsync ( 0 ) . unwrapOr ( 10 )
// unwrapped = 10⬆️ kembali ke atas
ResultAsync.andThen (metode) Ide yang sama seperti map di atas. Kecuali fungsi yang diterapkan harus mengembalikan Result atau ResultAsync .
ResultAsync.andThen ResultAsync
Ini berguna untuk ketika Anda perlu melakukan perhitungan selanjutnya menggunakan nilai T dalam, tetapi perhitungan itu mungkin gagal.
andThen sangat berguna sebagai alat untuk meratakan ResultAsync<ResultAsync<A, E2>, E1> ke dalam ResultAsync<A, E2> (lihat contoh di bawah).
Tanda tangan:
// 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 > { ... }
}Contoh
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." )
}
} )⬆️ kembali ke atas
ResultAsync.orElse (metode) Mengambil nilai Err dan memetakannya ke ResultAsync<T, SomeNewType> . Ini berguna untuk pemulihan kesalahan.
Tanda tangan:
class ResultAsync < T , E > {
orElse < U , A > (
callback : ( error : E ) => Result < U , A > | ResultAsync < U , A >
) : ResultAsync < U | T , A > { ... }
}⬆️ kembali ke atas
ResultAsync.match (metode) Diberikan 2 fungsi (satu untuk varian Ok dan satu untuk varian Err ) menjalankan fungsi yang cocok dengan varian ResultAsync .
Perbedaannya dengan Result.match adalah bahwa ia selalu mengembalikan Promise karena sifat asinkron dari ResultAsync .
Tanda tangan:
class ResultAsync < T , E > {
match < A , B = A > (
okCallback : ( value : T ) => A ,
errorCallback : ( error : E ) => B
) : Promise < A | B > => { ... }
}Contoh:
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⬆️ kembali ke atas
ResultAsync.andTee (metode) Mengambil ResultAsync<T, E> dan memungkinkan ResultAsync<T, E> melewati terlepas dari hasil dari fungsi lulus. Ini adalah cara praktis untuk menangani efek samping yang kegagalan atau keberhasilannya tidak boleh memengaruhi logika utama Anda seperti logging.
Tanda tangan:
class ResultAsync < T , E > {
andTee (
callback : ( value : T ) => unknown
) : ResultAsync < T , E > => { ... }
}Contoh:
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." )
}
} ) ) ⬆️ kembali ke atas
ResultAsync.andThrough (metode) Mirip dengan andTee kecuali untuk:
Tanda tangan:
class ResultAsync < T , E > {
andThrough < F > (
callback : ( value : T ) => Result < unknown , F > | ResultAsync < unknown , F > ,
) : ResultAsync < T , E | F > => { ... }
}Contoh:
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." )
}
} ) ) ⬆️ kembali ke atas
ResultAsync.combine (metode kelas statis) Kombinasikan Daftar ResultAsync S.
Jika Anda terbiasa dengan Promise.all , fungsi gabungan bekerja secara konseptual sama.
combine karya pada daftar yang heterogen dan homogen . Ini berarti bahwa Anda dapat memiliki daftar yang berisi berbagai jenis ResultAsync dan masih dapat menggabungkannya. Perhatikan bahwa Anda tidak dapat menggabungkan daftar yang berisi Result s dan ResultAsync s.
Fungsi gabungan mengambil daftar hasil dan mengembalikan hasil tunggal. Jika semua hasil dalam daftar Ok , maka nilai pengembalian akan menjadi Ok yang berisi daftar semua nilai Ok individu.
Jika hanya salah satu hasil dalam daftar adalah Err maka fungsi gabungan mengembalikan nilai err itu (itu sirkuit pendek dan mengembalikan kesalahan pertama yang ditemukannya).
Secara formal:
// 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 infinitumContoh:
const resultList : ResultAsync < number , never > [ ] =
[ okAsync ( 1 ) , okAsync ( 2 ) ]
const combinedList : ResultAsync < number [ ] , unknown > =
ResultAsync . combine ( resultList )Contoh dengan tupel:
/** @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 )⬆️ kembali ke atas
ResultAsync.combineWithAllErrors (metode kelas statis) Seperti combine tetapi tanpa hubungan pendek. Alih -alih hanya nilai kesalahan pertama, Anda mendapatkan daftar semua nilai kesalahan dari daftar hasil input.
Jika hanya beberapa hasil yang gagal, daftar kesalahan gabungan baru hanya akan berisi nilai kesalahan dari hasil yang gagal, yang berarti bahwa tidak ada jaminan panjang daftar kesalahan baru.
Tanda Tangan Fungsi:
// 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 infinitumContoh Penggunaan:
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()Tercerahkan . Anda tidak perlu menggunakan metode ini lagi.
Memungkinkan untuk membuka Result atau mengembalikan Err secara implisit, sehingga mengurangi boilerplate.
⬆️ kembali ke atas
fromThrowable Ekspor level atas Result.fromThrowable . Silakan temukan dokumentasi di hasil.
⬆️ kembali ke atas
fromAsyncThrowable Ekspor Tingkat Top ResultAsync.fromThrowable . Silakan temukan dokumentasi di resultasync.fromthrowable
⬆️ kembali ke atas
fromPromise Ekspor Tingkat Top ResultAsync.fromPromise . Silakan temukan dokumentasi di resultasync.frompromise
⬆️ kembali ke atas
fromSafePromise Ekspor Tingkat Top ResultAsync.fromSafePromise . Silakan temukan dokumentasi di resultasync.fromsafepromise
⬆️ kembali ke atas
safeTryDigunakan untuk secara implisit mengembalikan kesalahan dan mengurangi boilerplate.
Katakanlah kami menulis fungsi yang mengembalikan Result , dan dalam fungsi itu kami menyebut beberapa fungsi yang juga mengembalikan Result S dan kami memeriksa hasil tersebut untuk melihat apakah kami harus terus berjalan atau membatalkan. Biasanya, kami akan menulis seperti berikut ini.
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 ) ;
} Pada dasarnya, kita perlu mendefinisikan konstan untuk setiap hasil untuk memeriksa apakah itu Ok dan membaca .value .error .
Dengan safetry, kita dapat menyatakan 'kembali di sini jika itu Err , jika tidak buka bungkusnya di sini dan teruskan.' hanya dalam satu ekspresi.
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 } ` ) )
)
} )
} Untuk menggunakan safeTry , poinnya adalah sebagai berikut.
yield* <RESULT> untuk menyatakan 'Return <RESULT> Jika itu Err , sebaliknya mengevaluasi ke .value ' -nya 'safeTry Anda juga dapat menggunakan fungsi generator async untuk melewati blok async ke 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 } ` ) )
)
} )
}Untuk informasi lebih lanjut, lihat #448 dan #444
⬆️ kembali ke atas
Contoh Result memiliki dua metode yang tidak aman, yang disebut _unsafeUnwrap dan _unsafeUnwrapErr yang hanya boleh digunakan dalam lingkungan pengujian .
_unsafeUnwrap mengambil Result<T, E> dan mengembalikan T ketika hasilnya Ok , jika tidak ia melempar objek khusus.
_unsafeUnwrapErr mengambil Result<T, E> dan mengembalikan E ketika hasilnya adalah Err , jika tidak ia melempar objek khusus.
Dengan begitu Anda dapat melakukan sesuatu seperti:
expect ( myResult . _unsafeUnwrap ( ) ) . toBe ( someExpectation ) Namun, perhatikan bahwa contoh Result sebanding. Jadi, Anda tidak perlu membuka mereka untuk menegaskan harapan dalam tes Anda. Jadi Anda juga bisa melakukan hal seperti ini:
import { ok } from 'neverthrow'
// ...
expect ( callSomeFunctionThatReturnsAResult ( "with" , "some" , "args" ) ) . toEqual ( ok ( someExpectation ) ) ; Secara default, nilai yang dilemparkan tidak berisi jejak tumpukan. Ini karena Stack Trace Generation membuat pesan kesalahan menjadi lebih sulit untuk dipahami. Jika Anda ingin jejak tumpukan dihasilkan, hubungi _unsafeUnwrap dan / atau _unsafeUnwrapErr dengan objek konfigurasi:
_unsafeUnwrapErr ( {
withStackTrace : true ,
} )
// ^ Now the error object will have a `.stack` property containing the current stackJika Anda menemukan paket ini berguna, silakan pertimbangkan untuk mensponsori saya atau hanya membelikan saya kopi!
Meskipun paket itu disebut neverthrow , tolong jangan ambil ini secara harfiah. Saya hanya mendorong pengembang untuk berpikir sedikit lebih banyak tentang ergonomi dan penggunaan perangkat lunak apa pun yang mereka tulis.
Throw dan catching sangat mirip dengan menggunakan pernyataan goto - dengan kata lain; Itu membuat alasan tentang program Anda lebih keras. Kedua, dengan menggunakan throw Anda membuat asumsi bahwa penelepon fungsi Anda menerapkan catch . Ini adalah sumber kesalahan yang diketahui. Contoh: One Dev throw S dan Dev lain menggunakan fungsi tanpa pengetahuan sebelumnya bahwa fungsi akan melempar. Dengan demikian, dan Case Edge dibiarkan tidak ditangani dan sekarang Anda memiliki pengguna yang tidak bahagia, bos, kucing, dll.
Dengan semua yang dikatakan, pasti ada kasus penggunaan yang baik untuk melempar program Anda. Tapi jauh lebih sedikit dari yang Anda pikirkan.
Proyek Nevthrow tersedia sebagai open source di bawah ketentuan lisensi MIT.