| 1 | import { Chunk, Equal, Equivalence, flow, identity, Option, pipe, Result, String as Str } from "effect" |
| 2 | import { inspect } from "node:util" |
| 3 | import { describe, it } from "vitest" |
| 4 | import { |
| 5 | assertFailure, |
| 6 | assertFalse, |
| 7 | assertNone, |
| 8 | assertSome, |
| 9 | assertSuccess, |
| 10 | assertTrue, |
| 11 | deepStrictEqual, |
| 12 | strictEqual, |
| 13 | throws |
| 14 | } from "./utils/assert.ts" |
| 15 | |
| 16 | describe("Result", () => { |
| 17 | describe("Constructors", () => { |
| 18 | it("succeed creates a Success value", () => { |
| 19 | const result = Result.succeed(42) |
| 20 | assertTrue(Result.isSuccess(result)) |
| 21 | strictEqual(result.success, 42) |
| 22 | }) |
| 23 | |
| 24 | it("fail creates a Failure value", () => { |
| 25 | const result = Result.fail("err") |
| 26 | assertTrue(Result.isFailure(result)) |
| 27 | strictEqual(result.failure, "err") |
| 28 | }) |
| 29 | |
| 30 | it("void", () => { |
| 31 | deepStrictEqual(Result.void, Result.succeed(undefined)) |
| 32 | }) |
| 33 | |
| 34 | it("try", () => { |
| 35 | deepStrictEqual(Result.try(() => 1), Result.succeed(1)) |
| 36 | deepStrictEqual( |
| 37 | Result.try(() => { |
| 38 | throw "b" |
| 39 | }), |
| 40 | Result.fail("b") |
| 41 | ) |
| 42 | deepStrictEqual(Result.try({ try: () => 1, catch: (e) => new Error(String(e)) }), Result.succeed(1)) |
| 43 | deepStrictEqual( |
| 44 | Result.try({ |
| 45 | try: () => { |
| 46 | throw "b" |
| 47 | }, |
| 48 | catch: (e) => new Error(String(e)) |
| 49 | }), |
| 50 | Result.fail(new Error("b")) |
| 51 | ) |
| 52 | }) |
| 53 | |
| 54 | it("fromNullishOr", () => { |
| 55 | deepStrictEqual(Result.fromNullishOr(null, () => "fallback"), Result.fail("fallback")) |
| 56 | deepStrictEqual(Result.fromNullishOr(undefined, () => "fallback"), Result.fail("fallback")) |
| 57 | deepStrictEqual(Result.fromNullishOr(1, () => "fallback"), Result.succeed(1)) |
| 58 | // data-last |
| 59 | deepStrictEqual(pipe(null as string | null, Result.fromNullishOr(() => "fallback")), Result.fail("fallback")) |
| 60 | deepStrictEqual(pipe(1 as number | null, Result.fromNullishOr(() => "fallback")), Result.succeed(1)) |
| 61 | }) |
| 62 | |
| 63 | it("fromOption", () => { |
| 64 | deepStrictEqual(Result.fromOption(Option.none(), () => "none"), Result.fail("none")) |
| 65 | deepStrictEqual(Result.fromOption(Option.some(1), () => "none"), Result.succeed(1)) |
| 66 | // data-last |
| 67 | deepStrictEqual(pipe(Option.none(), Result.fromOption(() => "none")), Result.fail("none")) |
| 68 | deepStrictEqual(pipe(Option.some(1), Result.fromOption(() => "none")), Result.succeed(1)) |
| 69 | }) |
| 70 | |
| 71 | it("succeedNone", () => { |
| 72 | deepStrictEqual(Result.succeedNone, Result.succeed(Option.none())) |
| 73 | }) |
| 74 | |
| 75 | it("succeedSome", () => { |
| 76 | deepStrictEqual(Result.succeedSome(1), Result.succeed(Option.some(1))) |
| 77 | }) |
| 78 | }) |
| 79 | |
| 80 | describe("Methods", () => { |
| 81 | it("toString", () => { |
| 82 | strictEqual( |
| 83 | String(Result.succeed(1)), |
| 84 | `success(1)` |
| 85 | ) |
| 86 | strictEqual( |
| 87 | String(Result.fail("e")), |
| 88 | `failure("e")` |
| 89 | ) |
| 90 | strictEqual( |
| 91 | String(Result.succeed(Chunk.make(1, 2, 3))), |
| 92 | `success(Chunk([1,2,3]))` |
| 93 | ) |
| 94 | strictEqual( |
| 95 | String(Result.fail(Chunk.make(1, 2, 3))), |
| 96 | `failure(Chunk([1,2,3]))` |
| 97 | ) |
| 98 | strictEqual( |
| 99 | String(Result.succeed(Option.some(1))), |
| 100 | `success(some(1))` |
| 101 | ) |
| 102 | strictEqual( |
| 103 | String(Result.fail(Option.none())), |
| 104 | `failure(none())` |
| 105 | ) |
| 106 | }) |
| 107 | |
| 108 | it("toJSON", () => { |
| 109 | deepStrictEqual(Result.succeed(1).toJSON(), { _id: "Result", _tag: "Success", value: 1 }) |
| 110 | deepStrictEqual(Result.fail("e").toJSON(), { _id: "Result", _tag: "Failure", failure: "e" }) |
| 111 | }) |
| 112 | |
| 113 | it("inspect", () => { |
| 114 | deepStrictEqual(inspect(Result.succeed(1)), inspect({ _id: "Result", _tag: "Success", value: 1 })) |
| 115 | deepStrictEqual(inspect(Result.fail("e")), inspect({ _id: "Result", _tag: "Failure", failure: "e" })) |
| 116 | }) |
| 117 | |
| 118 | it("Equal trait compares matching tags and values", () => { |
| 119 | assertTrue(Equal.equals(Result.succeed(1), Result.succeed(1))) |
| 120 | assertTrue(Equal.equals(Result.fail("e"), Result.fail("e"))) |
| 121 | assertFalse(Equal.equals(Result.succeed(1), Result.fail("e"))) |
| 122 | assertFalse(Equal.equals(Result.fail("e"), Result.succeed(1))) |
| 123 | }) |
| 124 | |
| 125 | it("pipe composes Result operations", () => { |
| 126 | assertSuccess(Result.succeed(1).pipe(Result.map((n) => n + 1)), 2) |
| 127 | }) |
| 128 | }) |
| 129 | |
| 130 | describe("Type Guards", () => { |
| 131 | it("isResult", () => { |
| 132 | assertTrue(pipe(Result.succeed(1), Result.isResult)) |
| 133 | assertTrue(pipe(Result.fail("e"), Result.isResult)) |
| 134 | assertFalse(pipe(Option.some(1), Result.isResult)) |
| 135 | }) |
| 136 | |
| 137 | it("isFailure", () => { |
| 138 | assertFalse(Result.isFailure(Result.succeed(1))) |
| 139 | assertTrue(Result.isFailure(Result.fail(1))) |
| 140 | }) |
| 141 | |
| 142 | it("isSuccess", () => { |
| 143 | assertTrue(Result.isSuccess(Result.succeed(1))) |
| 144 | assertFalse(Result.isSuccess(Result.fail(1))) |
| 145 | }) |
| 146 | }) |
| 147 | |
| 148 | describe("Getters", () => { |
| 149 | it("getSuccess", () => { |
| 150 | assertSome(pipe(Result.succeed(1), Result.getSuccess), 1) |
| 151 | assertNone(pipe(Result.fail("a"), Result.getSuccess)) |
| 152 | }) |
| 153 | |
| 154 | it("getFailure", () => { |
| 155 | assertNone(pipe(Result.succeed(1), Result.getFailure)) |
| 156 | assertSome(pipe(Result.fail("e"), Result.getFailure), "e") |
| 157 | }) |
| 158 | |
| 159 | it("getOrElse", () => { |
| 160 | strictEqual(Result.getOrElse(Result.succeed(1), (error) => error + "!"), 1) |
| 161 | strictEqual(Result.getOrElse(Result.fail("not a number"), (error) => error + "!"), "not a number!") |
| 162 | // data-last |
| 163 | strictEqual(pipe(Result.succeed(1), Result.getOrElse((error) => error + "!")), 1) |
| 164 | strictEqual(pipe(Result.fail("not a number"), Result.getOrElse((error) => error + "!")), "not a number!") |
| 165 | }) |
| 166 | |
| 167 | it("getOrNull", () => { |
| 168 | strictEqual(Result.getOrNull(Result.succeed(1)), 1) |
| 169 | strictEqual(Result.getOrNull(Result.fail("a")), null) |
| 170 | }) |
| 171 | |
| 172 | it("getOrUndefined", () => { |
| 173 | strictEqual(Result.getOrUndefined(Result.succeed(1)), 1) |
| 174 | strictEqual(Result.getOrUndefined(Result.fail("a")), undefined) |
| 175 | }) |
| 176 | |
| 177 | it("getOrThrowWith", () => { |
| 178 | strictEqual(pipe(Result.succeed(1), Result.getOrThrowWith((e) => new Error(`Unexpected Err: ${e}`))), 1) |
| 179 | throws(() => pipe(Result.fail("e"), Result.getOrThrowWith((e) => new Error(`Unexpected Err: ${e}`)))) |
| 180 | // data-first |
| 181 | strictEqual(Result.getOrThrowWith(Result.succeed(1), (e) => new Error(`Unexpected Err: ${e}`)), 1) |
| 182 | throws(() => Result.getOrThrowWith(Result.fail("e"), (e) => new Error(`Unexpected Err: ${e}`))) |
| 183 | }) |
| 184 | |
| 185 | it("getOrThrow", () => { |
| 186 | strictEqual(pipe(Result.succeed(1), Result.getOrThrow), 1) |
| 187 | throws(() => pipe(Result.fail(new Error("e")), Result.getOrThrow), new Error("e")) |
| 188 | }) |
| 189 | |
| 190 | it("merge", () => { |
| 191 | deepStrictEqual(Result.merge(Result.succeed(1)), 1) |
| 192 | deepStrictEqual(Result.merge(Result.fail("a")), "a") |
| 193 | }) |
| 194 | }) |
| 195 | |
| 196 | describe("Mapping", () => { |
| 197 | it("map", () => { |
| 198 | const f = Result.map(Str.length) |
| 199 | const failure = Result.fail("s") |
| 200 | assertSuccess(pipe(Result.succeed("abc"), f), 3) |
| 201 | strictEqual(pipe(failure, f), failure) |
| 202 | // data-first |
| 203 | assertSuccess(Result.map(Result.succeed("abc"), Str.length), 3) |
| 204 | strictEqual(Result.map(failure, Str.length), failure) |
| 205 | }) |
| 206 | |
| 207 | it("mapBoth", () => { |
| 208 | const f = Result.mapBoth({ |
| 209 | onFailure: Str.length, |
| 210 | onSuccess: (n: number) => n > 2 |
| 211 | }) |
| 212 | assertSuccess(pipe(Result.succeed(1), f), false) |
| 213 | assertFailure(pipe(Result.fail("a"), f), 1) |
| 214 | // data-first |
| 215 | assertSuccess( |
| 216 | Result.mapBoth(Result.succeed(1), { |
| 217 | onFailure: Str.length, |
| 218 | onSuccess: (n: number) => n > 2 |
| 219 | }), |
| 220 | false |
| 221 | ) |
| 222 | assertFailure( |
| 223 | Result.mapBoth(Result.fail("a"), { |
| 224 | onFailure: Str.length, |
| 225 | onSuccess: (n: number) => n > 2 |
| 226 | }), |
| 227 | 1 |
| 228 | ) |
| 229 | }) |
| 230 | |
| 231 | it("mapError", () => { |
| 232 | const f = Result.mapError((n: number) => n * 2) |
| 233 | const success = Result.succeed("a") |
| 234 | strictEqual(pipe(success, f), success) |
| 235 | assertFailure(pipe(Result.fail(1), f), 2) |
| 236 | // data-first |
| 237 | strictEqual(Result.mapError(success, (n: number) => n * 2), success) |
| 238 | assertFailure(Result.mapError(Result.fail(1), (n) => n * 2), 2) |
| 239 | }) |
| 240 | |
| 241 | it("tap", () => { |
| 242 | let sideEffect = 0 |
| 243 | const success = pipe( |
| 244 | Result.succeed(42), |
| 245 | Result.tap((n) => { |
| 246 | sideEffect = n |
| 247 | }) |
| 248 | ) |
| 249 | assertSuccess(success, 42) |
| 250 | strictEqual(sideEffect, 42) |
| 251 | |
| 252 | // failure path: side effect should not run |
| 253 | let failureSideEffect = 0 |
| 254 | const failure = pipe( |
| 255 | Result.fail("err") as Result.Result<number, string>, |
| 256 | Result.tap((n) => { |
| 257 | failureSideEffect = n |
| 258 | }) |
| 259 | ) |
| 260 | assertFailure(failure, "err") |
| 261 | strictEqual(failureSideEffect, 0) |
| 262 | |
| 263 | // data-first |
| 264 | let sideEffect2 = 0 |
| 265 | const success2 = Result.tap(Result.succeed(10), (n) => { |
| 266 | sideEffect2 = n |
| 267 | }) |
| 268 | assertSuccess(success2, 10) |
| 269 | strictEqual(sideEffect2, 10) |
| 270 | }) |
| 271 | }) |
| 272 | |
| 273 | describe("Pattern Matching", () => { |
| 274 | it("match handles failure and success cases", () => { |
| 275 | const onFailure = (s: string) => `failure${s.length}` |
| 276 | const onSuccess = (s: string) => `success${s.length}` |
| 277 | const match = Result.match({ onFailure, onSuccess }) |
| 278 | strictEqual(match(Result.fail("abc")), "failure3") |
| 279 | strictEqual(match(Result.succeed("abc")), "success3") |
| 280 | // data-first |
| 281 | strictEqual(Result.match(Result.fail("abc"), { onFailure, onSuccess }), "failure3") |
| 282 | strictEqual(Result.match(Result.succeed("abc"), { onFailure, onSuccess }), "success3") |
| 283 | }) |
| 284 | }) |
| 285 | |
| 286 | describe("Utils", () => { |
| 287 | it("flip", () => { |
| 288 | assertFailure(Result.flip(Result.succeed("a")), "a") |
| 289 | assertSuccess(Result.flip(Result.fail("b")), "b") |
| 290 | }) |
| 291 | |
| 292 | it("liftPredicate", () => { |
| 293 | const isPositivePredicate = (n: number) => n > 0 |
| 294 | const onPositivePredicateError = (n: number) => `${n} is not positive` |
| 295 | const isNumberRefinement = (n: string | number): n is number => typeof n === "number" |
| 296 | const onNumberRefinementError = (n: string | number) => `${n} is not a number` |
| 297 | |
| 298 | assertSuccess( |
| 299 | pipe(1, Result.liftPredicate(isPositivePredicate, onPositivePredicateError)), |
| 300 | 1 |
| 301 | ) |
| 302 | assertFailure( |
| 303 | pipe(-1, Result.liftPredicate(isPositivePredicate, onPositivePredicateError)), |
| 304 | "-1 is not positive" |
| 305 | ) |
| 306 | assertSuccess( |
| 307 | pipe(1, Result.liftPredicate(isNumberRefinement, onNumberRefinementError)), |
| 308 | 1 |
| 309 | ) |
| 310 | assertFailure( |
| 311 | pipe("string", Result.liftPredicate(isNumberRefinement, onNumberRefinementError)), |
| 312 | "string is not a number" |
| 313 | ) |
| 314 | |
| 315 | assertSuccess( |
| 316 | Result.liftPredicate(1, isPositivePredicate, onPositivePredicateError), |
| 317 | 1 |
| 318 | ) |
| 319 | assertFailure( |
| 320 | Result.liftPredicate(-1, isPositivePredicate, onPositivePredicateError), |
| 321 | "-1 is not positive" |
| 322 | ) |
| 323 | assertSuccess( |
| 324 | Result.liftPredicate(1, isNumberRefinement, onNumberRefinementError), |
| 325 | 1 |
| 326 | ) |
| 327 | assertFailure( |
| 328 | Result.liftPredicate("string", isNumberRefinement, onNumberRefinementError), |
| 329 | "string is not a number" |
| 330 | ) |
| 331 | }) |
| 332 | }) |
| 333 | |
| 334 | describe("Filtering", () => { |
| 335 | it("filterOrFail", () => { |
| 336 | deepStrictEqual(Result.filterOrFail(Result.succeed(1), (n) => n > 0, () => "a"), Result.succeed(1)) |
| 337 | deepStrictEqual(Result.filterOrFail(Result.succeed(1), (n) => n > 1, () => "a"), Result.fail("a")) |
| 338 | deepStrictEqual(Result.filterOrFail(Result.fail(1), (n) => n > 0, () => "a"), Result.fail(1)) |
| 339 | |
| 340 | deepStrictEqual(Result.succeed(1).pipe(Result.filterOrFail((n) => n > 0, () => "a")), Result.succeed(1)) |
| 341 | deepStrictEqual(Result.succeed(1).pipe(Result.filterOrFail((n) => n > 1, () => "a")), Result.fail("a")) |
| 342 | deepStrictEqual(Result.fail(1).pipe(Result.filterOrFail((n) => n > 0, () => "a")), Result.fail(1)) |
| 343 | }) |
| 344 | }) |
| 345 | |
| 346 | describe("Equivalence", () => { |
| 347 | it("makeEquivalence compares successes and failures by channel", () => { |
| 348 | const isEquivalent = Result.makeEquivalence( |
| 349 | Equivalence.strictEqual<number>(), |
| 350 | Equivalence.strictEqual<string>() |
| 351 | ) |
| 352 | deepStrictEqual(isEquivalent(Result.succeed(1), Result.succeed(1)), true) |
| 353 | deepStrictEqual(isEquivalent(Result.succeed(1), Result.succeed(2)), false) |
| 354 | deepStrictEqual(isEquivalent(Result.succeed(1), Result.fail("foo")), false) |
| 355 | deepStrictEqual(isEquivalent(Result.fail("foo"), Result.fail("foo")), true) |
| 356 | deepStrictEqual(isEquivalent(Result.fail("foo"), Result.fail("bar")), false) |
| 357 | deepStrictEqual(isEquivalent(Result.fail("foo"), Result.succeed(1)), false) |
| 358 | }) |
| 359 | }) |
| 360 | |
| 361 | describe("Sequencing", () => { |
| 362 | it("flatMap", () => { |
| 363 | const f = Result.flatMap(flow(Str.length, Result.succeed)) |
| 364 | assertSuccess(pipe(Result.succeed("abc"), f), 3) |
| 365 | assertFailure(pipe(Result.fail("maError"), f), "maError") |
| 366 | // data-first |
| 367 | assertSuccess(Result.flatMap(Result.succeed("abc"), flow(Str.length, Result.succeed)), 3) |
| 368 | assertFailure(Result.flatMap(Result.fail("maError"), flow(Str.length, Result.succeed)), "maError") |
| 369 | }) |
| 370 | |
| 371 | it("andThen replaces successes with Result or plain values", () => { |
| 372 | assertSuccess(pipe(Result.succeed(1), Result.andThen(() => Result.succeed(2))), 2) |
| 373 | assertSuccess(pipe(Result.succeed(1), Result.andThen(Result.succeed(2))), 2) |
| 374 | assertSuccess(pipe(Result.succeed(1), Result.andThen(2)), 2) |
| 375 | assertSuccess(pipe(Result.succeed(1), Result.andThen(() => 2)), 2) |
| 376 | assertSuccess(pipe(Result.succeed(1), Result.andThen((a) => a)), 1) |
| 377 | assertSuccess(Result.andThen(Result.succeed(1), () => Result.succeed(2)), 2) |
| 378 | assertSuccess(Result.andThen(Result.succeed(1), Result.succeed(2)), 2) |
| 379 | assertSuccess(Result.andThen(Result.succeed(1), () => 2), 2) |
| 380 | assertSuccess(Result.andThen(Result.succeed(1), 2), 2) |
| 381 | assertSuccess(Result.andThen(Result.succeed(1), (a) => a), 1) |
| 382 | }) |
| 383 | |
| 384 | it("all", () => { |
| 385 | // tuples and arrays |
| 386 | assertSuccess(Result.all([]), []) |
| 387 | assertSuccess(Result.all([Result.succeed(1)]), [1]) |
| 388 | assertSuccess(Result.all([Result.succeed(1), Result.succeed(true)]), [1, true]) |
| 389 | assertFailure(Result.all([Result.succeed(1), Result.fail("e")]), "e") |
| 390 | // structs and records |
| 391 | assertSuccess(Result.all({}), {}) |
| 392 | assertSuccess(Result.all({ a: Result.succeed(1) }), { a: 1 }) |
| 393 | assertSuccess(Result.all({ a: Result.succeed(1), b: Result.succeed(true) }), { a: 1, b: true }) |
| 394 | assertFailure(Result.all({ a: Result.succeed(1), b: Result.fail("e") }), "e") |
| 395 | // iterables |
| 396 | const iterable = function*() { |
| 397 | yield Result.succeed(1) |
| 398 | yield Result.succeed(2) |
| 399 | yield Result.succeed(3) |
| 400 | } |
| 401 | assertSuccess(Result.all(iterable()), [1, 2, 3]) |
| 402 | |
| 403 | const iterableWithFailure = function*() { |
| 404 | yield Result.succeed(1) |
| 405 | yield Result.fail("e") |
| 406 | yield Result.succeed(3) |
| 407 | } |
| 408 | assertFailure(Result.all(iterableWithFailure()), "e") |
| 409 | }) |
| 410 | }) |
| 411 | |
| 412 | describe("Error Handling", () => { |
| 413 | it("orElse recovers failures and preserves successes", () => { |
| 414 | assertSuccess(pipe(Result.succeed(1), Result.orElse(() => Result.succeed(2))), 1) |
| 415 | assertSuccess(pipe(Result.succeed(1), Result.orElse(() => Result.fail("b"))), 1) |
| 416 | assertSuccess(pipe(Result.fail("a"), Result.orElse(() => Result.succeed(2))), 2) |
| 417 | assertFailure(pipe(Result.fail("a"), Result.orElse(() => Result.fail("b"))), "b") |
| 418 | // data-first |
| 419 | assertSuccess(Result.orElse(Result.succeed(1), () => Result.succeed(2)), 1) |
| 420 | assertSuccess(Result.orElse(Result.fail("a"), () => Result.succeed(2)), 2) |
| 421 | assertFailure(Result.orElse(Result.fail("a"), () => Result.fail("b")), "b") |
| 422 | }) |
| 423 | }) |
| 424 | |
| 425 | describe("Do Notation", () => { |
| 426 | it("Do", () => { |
| 427 | assertSuccess(Result.Do, {}) |
| 428 | }) |
| 429 | |
| 430 | it("bindTo", () => { |
| 431 | assertSuccess(pipe(Result.succeed(1), Result.bindTo("a")), { a: 1 }) |
| 432 | assertFailure(pipe(Result.fail("left"), Result.bindTo("a")), "left") |
| 433 | |
| 434 | assertSuccess(pipe(Result.succeed(1), Result.bindTo("__proto__")), { ["__proto__"]: 1 }) |
| 435 | }) |
| 436 | |
| 437 | it("bind", () => { |
| 438 | assertSuccess(pipe(Result.succeed(1), Result.bindTo("a"), Result.bind("b", ({ a }) => Result.succeed(a + 1))), { |
| 439 | a: 1, |
| 440 | b: 2 |
| 441 | }) |
| 442 | assertFailure( |
| 443 | pipe(Result.succeed(1), Result.bindTo("a"), Result.bind("b", () => Result.fail("left"))), |
| 444 | "left" |
| 445 | ) |
| 446 | assertFailure( |
| 447 | pipe(Result.fail("left"), Result.bindTo("a"), Result.bind("b", () => Result.succeed(2))), |
| 448 | "left" |
| 449 | ) |
| 450 | assertSuccess(pipe(Result.Do, Result.bind("__proto__", () => Result.succeed(1))), { ["__proto__"]: 1 }) |
| 451 | }) |
| 452 | |
| 453 | it("let", () => { |
| 454 | assertSuccess(pipe(Result.succeed(1), Result.bindTo("a"), Result.let("b", ({ a }) => a + 1)), { a: 1, b: 2 }) |
| 455 | assertFailure( |
| 456 | pipe(Result.fail("left"), Result.bindTo("a"), Result.let("b", () => 2)), |
| 457 | "left" |
| 458 | ) |
| 459 | assertSuccess(pipe(Result.Do, Result.let("__proto__", () => 1)), { ["__proto__"]: 1 }) |
| 460 | }) |
| 461 | }) |
| 462 | |
| 463 | describe("Generators", () => { |
| 464 | it("gen", () => { |
| 465 | const a = Result.gen(function*() { |
| 466 | const x = yield* Result.succeed(1) |
| 467 | const y = yield* Result.succeed(2) |
| 468 | return x + y |
| 469 | }) |
| 470 | const b = Result.gen(function*() { |
| 471 | return 10 |
| 472 | }) |
| 473 | const c = Result.gen(function*() { |
| 474 | yield* Result.succeed(1) |
| 475 | yield* Result.succeed(2) |
| 476 | }) |
| 477 | const d = Result.gen(function*() { |
| 478 | yield* Result.succeed(1) |
| 479 | return yield* Result.succeed(2) |
| 480 | }) |
| 481 | const e = Result.gen(function*() { |
| 482 | yield* Result.succeed(1) |
| 483 | yield* Result.fail("err") |
| 484 | return yield* Result.succeed(2) |
| 485 | }) |
| 486 | const f = Result.gen(function*() { |
| 487 | yield* Result.fail("err") |
| 488 | }) |
| 489 | const g = Result.gen({ context: "testContext" as const }, function*() { |
| 490 | return yield* Result.succeed(this.context) |
| 491 | }) |
| 492 | |
| 493 | assertSuccess(a, 3) |
| 494 | assertSuccess(b, 10) |
| 495 | assertSuccess(c, undefined) |
| 496 | assertSuccess(d, 2) |
| 497 | assertFailure(e, "err") |
| 498 | assertFailure(f, "err") |
| 499 | assertSuccess(g, "testContext") |
| 500 | }) |
| 501 | }) |
| 502 | |
| 503 | describe("Transposing", () => { |
| 504 | it("transposeOption", () => { |
| 505 | assertSuccess(Result.transposeOption(Option.some(Result.succeed(1))), Option.some(1)) |
| 506 | assertSuccess(Result.transposeOption(Option.none()), Option.none()) |
| 507 | assertFailure(Result.transposeOption(Option.some(Result.fail("e"))), "e") |
| 508 | }) |
| 509 | |
| 510 | it("transposeMapOption", () => { |
| 511 | assertSuccess(Result.transposeMapOption(Option.some(Result.succeed(1)), identity), Option.some(1)) |
| 512 | assertSuccess(Result.transposeMapOption(Option.none(), identity), Option.none()) |
| 513 | assertFailure(Result.transposeMapOption(Option.some(Result.fail("e")), identity), "e") |
| 514 | // data-last |
| 515 | assertSuccess(pipe(Option.some(1), Result.transposeMapOption(Result.succeed)), Option.some(1)) |
| 516 | assertSuccess(pipe(Option.none<number>(), Result.transposeMapOption(Result.succeed)), Option.none()) |
| 517 | }) |
| 518 | }) |
| 519 | }) |