| 1 | import { |
| 2 | assertFalse, |
| 3 | assertNone, |
| 4 | assertSome, |
| 5 | assertTrue, |
| 6 | deepStrictEqual, |
| 7 | strictEqual, |
| 8 | throws |
| 9 | } from "@effect/vitest/utils" |
| 10 | import { Chunk, Equal, Equivalence, Hash, Number, Number as Num, Option, pipe, Result, String as Str } from "effect" |
| 11 | import { describe, it } from "vitest" |
| 12 | |
| 13 | const gt2 = (n: number): boolean => n > 2 |
| 14 | |
| 15 | describe("Option", () => { |
| 16 | it("makeReducer", () => { |
| 17 | const R = Option.makeReducer(Number.ReducerSum) |
| 18 | |
| 19 | deepStrictEqual(R.combine(Option.some(1), Option.some(2)), Option.some(3)) |
| 20 | deepStrictEqual(R.combine(Option.some(1), Option.none()), Option.some(1)) |
| 21 | deepStrictEqual(R.combine(Option.none(), Option.some(2)), Option.some(2)) |
| 22 | deepStrictEqual(R.combine(Option.none(), Option.none()), Option.none()) |
| 23 | }) |
| 24 | |
| 25 | it("makeReducerFailFast", () => { |
| 26 | const R = Option.makeReducerFailFast(Number.ReducerSum) |
| 27 | |
| 28 | deepStrictEqual(R.combine(Option.some(1), Option.some(2)), Option.some(3)) |
| 29 | deepStrictEqual(R.combine(Option.some(1), Option.none()), Option.none()) |
| 30 | deepStrictEqual(R.combine(Option.none(), Option.some(2)), Option.none()) |
| 31 | deepStrictEqual(R.combine(Option.none(), Option.none()), Option.none()) |
| 32 | |
| 33 | deepStrictEqual(R.combine(Option.none(), R.initialValue), Option.none()) |
| 34 | deepStrictEqual(R.combine(R.initialValue, Option.none()), Option.none()) |
| 35 | deepStrictEqual(R.combine(Option.some(1), R.initialValue), Option.some(1)) |
| 36 | deepStrictEqual(R.combine(R.initialValue, Option.some(1)), Option.some(1)) |
| 37 | }) |
| 38 | |
| 39 | it("gen", () => { |
| 40 | const a = Option.gen(function*() { |
| 41 | const x = yield* Option.some(1) |
| 42 | const y = yield* Option.some(2) |
| 43 | return x + y |
| 44 | }) |
| 45 | const b = Option.gen(function*() { |
| 46 | return 10 |
| 47 | }) |
| 48 | const c = Option.gen(function*() { |
| 49 | yield* Option.some(1) |
| 50 | yield* Option.some(2) |
| 51 | }) |
| 52 | const d = Option.gen(function*() { |
| 53 | yield* Option.some(1) |
| 54 | return yield* Option.some(2) |
| 55 | }) |
| 56 | const e = Option.gen(function*() { |
| 57 | yield* Option.some(1) |
| 58 | yield* Option.none() |
| 59 | return yield* Option.some(2) |
| 60 | }) |
| 61 | const f = Option.gen(function*() { |
| 62 | yield* Option.none() |
| 63 | }) |
| 64 | const g = Option.gen({ ctx: "testContext" as const }, function*() { |
| 65 | return yield* Option.some(this.ctx) |
| 66 | }) |
| 67 | |
| 68 | assertSome(a, 3) |
| 69 | assertSome(b, 10) |
| 70 | assertSome(c, undefined) |
| 71 | assertSome(d, 2) |
| 72 | assertNone(e) |
| 73 | assertNone(f) |
| 74 | assertSome(g, "testContext") |
| 75 | }) |
| 76 | |
| 77 | it("toString", () => { |
| 78 | strictEqual( |
| 79 | String(Option.none()), |
| 80 | `none()` |
| 81 | ) |
| 82 | strictEqual( |
| 83 | String(Option.some(1)), |
| 84 | `some(1)` |
| 85 | ) |
| 86 | strictEqual( |
| 87 | String(Option.some(Chunk.make(1, 2, 3))), |
| 88 | `some(Chunk([1,2,3]))` |
| 89 | ) |
| 90 | }) |
| 91 | |
| 92 | it("toJSON", () => { |
| 93 | deepStrictEqual(Option.none().toJSON(), { _id: "Option", _tag: "None" }) |
| 94 | deepStrictEqual(Option.some(1).toJSON(), { _id: "Option", _tag: "Some", value: 1 }) |
| 95 | }) |
| 96 | |
| 97 | it("inspect", () => { |
| 98 | if (typeof window !== "undefined") { |
| 99 | return |
| 100 | } |
| 101 | // eslint-disable-next-line @typescript-eslint/no-require-imports |
| 102 | const { inspect } = require("node:util") |
| 103 | deepStrictEqual(inspect(Option.none()), inspect({ _id: "Option", _tag: "None" })) |
| 104 | deepStrictEqual(inspect(Option.some(1)), inspect({ _id: "Option", _tag: "Some", value: 1 })) |
| 105 | }) |
| 106 | |
| 107 | it("verify structural equality", () => { |
| 108 | assertTrue(Equal.equals(Option.some(1), Option.some(1))) |
| 109 | assertFalse(Equal.equals(Option.some(1), Option.some(2))) |
| 110 | assertTrue(Equal.equals(Option.none(), Option.none())) |
| 111 | }) |
| 112 | |
| 113 | it("verify hash equality", () => { |
| 114 | strictEqual(Hash.hash(Option.some(1)), Hash.hash(Option.some(1))) |
| 115 | strictEqual(Hash.hash(Option.some(1)) === Hash.hash(Option.some(2)), false) |
| 116 | strictEqual(Hash.hash(Option.none()), Hash.hash(Option.none())) |
| 117 | }) |
| 118 | |
| 119 | it("getSuccess", () => { |
| 120 | assertSome(Option.getSuccess(Result.succeed(1)), 1) |
| 121 | assertNone(Option.getSuccess(Result.fail("a"))) |
| 122 | }) |
| 123 | |
| 124 | it("getFailure", () => { |
| 125 | assertNone(Option.getFailure(Result.succeed(1))) |
| 126 | assertSome(Option.getFailure(Result.fail("a")), "a") |
| 127 | }) |
| 128 | |
| 129 | it("toRefinement", () => { |
| 130 | const f = ( |
| 131 | s: string | number |
| 132 | ): Option.Option<string> => (typeof s === "string" ? Option.some(s) : Option.none()) |
| 133 | const isString = Option.toRefinement(f) |
| 134 | |
| 135 | assertTrue(isString("s")) |
| 136 | assertFalse(isString(1)) |
| 137 | |
| 138 | type A = { readonly type: "A" } |
| 139 | type B = { readonly type: "B" } |
| 140 | type C = A | B |
| 141 | const isA = Option.toRefinement<C, A>((c) => (c.type === "A" ? Option.some(c) : Option.none())) |
| 142 | |
| 143 | assertTrue(isA({ type: "A" })) |
| 144 | assertFalse(isA({ type: "B" })) |
| 145 | }) |
| 146 | |
| 147 | it("isOption", () => { |
| 148 | assertTrue(pipe(Option.some(1), Option.isOption)) |
| 149 | assertTrue(pipe(Option.none(), Option.isOption)) |
| 150 | assertFalse(pipe(Result.succeed(1), Option.isOption)) |
| 151 | }) |
| 152 | |
| 153 | it("firstSomeOf", () => { |
| 154 | assertNone(Option.firstSomeOf([])) |
| 155 | assertSome(Option.firstSomeOf([Option.some(1)]), 1) |
| 156 | assertNone(Option.firstSomeOf([Option.none()])) |
| 157 | assertSome( |
| 158 | Option.firstSomeOf([Option.none(), Option.none(), Option.none(), Option.none(), Option.some(1)]), |
| 159 | 1 |
| 160 | ) |
| 161 | assertNone( |
| 162 | Option.firstSomeOf([Option.none(), Option.none(), Option.none(), Option.none()]) |
| 163 | ) |
| 164 | }) |
| 165 | |
| 166 | it("orElseResult", () => { |
| 167 | assertSome(pipe(Option.some(1), Option.orElseResult(() => Option.some(2))), Result.fail(1)) |
| 168 | assertSome(pipe(Option.some(1), Option.orElseResult(() => Option.none())), Result.fail(1)) |
| 169 | assertSome(pipe(Option.none(), Option.orElseResult(() => Option.some(2))), Result.succeed(2)) |
| 170 | assertNone(pipe(Option.none(), Option.orElseResult(() => Option.none()))) |
| 171 | }) |
| 172 | |
| 173 | it("orElseSome", () => { |
| 174 | assertSome(pipe(Option.some(1), Option.orElseSome(() => 2)), 1) |
| 175 | assertSome(pipe(Option.none(), Option.orElseSome(() => 2)), 2) |
| 176 | }) |
| 177 | |
| 178 | it("getOrThrow", () => { |
| 179 | strictEqual(pipe(Option.some(1), Option.getOrThrow), 1) |
| 180 | throws(() => pipe(Option.none(), Option.getOrThrow), new Error("getOrThrow called on a None")) |
| 181 | }) |
| 182 | |
| 183 | it("getOrThrowWith", () => { |
| 184 | strictEqual(pipe(Option.some(1), Option.getOrThrowWith(() => new Error("Unexpected None"))), 1) |
| 185 | throws( |
| 186 | () => pipe(Option.none(), Option.getOrThrowWith(() => new Error("Unexpected None"))), |
| 187 | new Error("Unexpected None") |
| 188 | ) |
| 189 | }) |
| 190 | |
| 191 | it("unit", () => { |
| 192 | assertSome(Option.void, undefined) |
| 193 | }) |
| 194 | |
| 195 | it("product", () => { |
| 196 | const product = Option.product |
| 197 | assertNone(product(Option.none(), Option.none())) |
| 198 | assertNone(product(Option.some(1), Option.none())) |
| 199 | assertNone(product(Option.none(), Option.some("a"))) |
| 200 | assertSome(product(Option.some(1), Option.some("a")), [1, "a"]) |
| 201 | }) |
| 202 | |
| 203 | it("productMany", () => { |
| 204 | const productMany = Option.productMany |
| 205 | assertNone(productMany(Option.none(), [])) |
| 206 | assertSome(productMany(Option.some(1), []), [1]) |
| 207 | assertNone(productMany(Option.some(1), [Option.none()])) |
| 208 | assertSome(productMany(Option.some(1), [Option.some(2)]), [1, 2]) |
| 209 | }) |
| 210 | |
| 211 | it("fromIterable", () => { |
| 212 | assertNone(Option.fromIterable([])) |
| 213 | assertSome(Option.fromIterable(["a"]), "a") |
| 214 | }) |
| 215 | |
| 216 | it("map", () => { |
| 217 | assertSome(pipe(Option.some(2), Option.map((n) => n * 2)), 4) |
| 218 | assertNone(pipe(Option.none(), Option.map((n) => n * 2))) |
| 219 | }) |
| 220 | |
| 221 | it("flatMap", () => { |
| 222 | const f = (n: number) => Option.some(n * 2) |
| 223 | const g = () => Option.none() |
| 224 | assertSome(pipe(Option.some(1), Option.flatMap(f)), 2) |
| 225 | assertNone(pipe(Option.none(), Option.flatMap(f))) |
| 226 | assertNone(pipe(Option.some(1), Option.flatMap(g))) |
| 227 | assertNone(pipe(Option.none(), Option.flatMap(g))) |
| 228 | }) |
| 229 | |
| 230 | it("andThen", () => { |
| 231 | assertSome(pipe(Option.some(1), Option.andThen(() => Option.some(2))), 2) |
| 232 | assertSome(pipe(Option.some(1), Option.andThen(Option.some(2))), 2) |
| 233 | assertSome(pipe(Option.some(1), Option.andThen(2)), 2) |
| 234 | assertSome(pipe(Option.some(1), Option.andThen(() => 2)), 2) |
| 235 | assertSome(pipe(Option.some(1), Option.andThen((a) => a)), 1) |
| 236 | assertSome(Option.andThen(Option.some(1), () => Option.some(2)), 2) |
| 237 | assertSome(Option.andThen(Option.some(1), Option.some(2)), 2) |
| 238 | assertSome(Option.andThen(Option.some(1), 2), 2) |
| 239 | assertSome(Option.andThen(Option.some(1), () => 2), 2) |
| 240 | assertSome(Option.andThen(Option.some(1), (a) => a), 1) |
| 241 | }) |
| 242 | |
| 243 | it("orElse", () => { |
| 244 | const assertOrElse = ( |
| 245 | a: Option.Option<number>, |
| 246 | b: Option.Option<number>, |
| 247 | expected: Option.Option<number> |
| 248 | ) => { |
| 249 | deepStrictEqual(pipe(a, Option.orElse(() => b)), expected) |
| 250 | } |
| 251 | assertOrElse(Option.some(1), Option.some(2), Option.some(1)) |
| 252 | assertOrElse(Option.some(1), Option.none(), Option.some(1)) |
| 253 | assertOrElse(Option.none(), Option.some(2), Option.some(2)) |
| 254 | assertOrElse(Option.none(), Option.none(), Option.none()) |
| 255 | }) |
| 256 | |
| 257 | it("partitionMap", () => { |
| 258 | const f = (n: number) => (gt2(n) ? Result.succeed(n + 1) : Result.fail(n - 1)) |
| 259 | deepStrictEqual(pipe(Option.none(), Option.partitionMap(f)), [Option.none(), Option.none()]) |
| 260 | deepStrictEqual(pipe(Option.some(1), Option.partitionMap(f)), [Option.none(), Option.some(0)]) |
| 261 | deepStrictEqual(pipe(Option.some(3), Option.partitionMap(f)), [Option.some(4), Option.none()]) |
| 262 | }) |
| 263 | |
| 264 | it("filterMap", () => { |
| 265 | const f = (n: number) => (gt2(n) ? Result.succeed(n + 1) : Result.failVoid) |
| 266 | assertNone(pipe(Option.none(), Option.filterMap(f))) |
| 267 | assertNone(pipe(Option.some(1), Option.filterMap(f))) |
| 268 | assertSome(pipe(Option.some(3), Option.filterMap(f)), 4) |
| 269 | }) |
| 270 | |
| 271 | it("match", () => { |
| 272 | const onNone = () => "none" |
| 273 | const onSome = (s: string) => `some${s.length}` |
| 274 | const match = Option.match({ onNone, onSome }) |
| 275 | strictEqual(match(Option.none()), "none") |
| 276 | strictEqual(match(Option.some("abc")), "some3") |
| 277 | }) |
| 278 | |
| 279 | it("getOrElse", () => { |
| 280 | strictEqual(pipe(Option.some(1), Option.getOrElse(() => 0)), 1) |
| 281 | strictEqual(pipe(Option.none(), Option.getOrElse(() => 0)), 0) |
| 282 | }) |
| 283 | |
| 284 | it("getOrNull", () => { |
| 285 | strictEqual(Option.getOrNull(Option.none()), null) |
| 286 | strictEqual(Option.getOrNull(Option.some(1)), 1) |
| 287 | }) |
| 288 | |
| 289 | it("getOrUndefined", () => { |
| 290 | strictEqual(Option.getOrUndefined(Option.none()), undefined) |
| 291 | strictEqual(Option.getOrUndefined(Option.some(1)), 1) |
| 292 | }) |
| 293 | |
| 294 | it("makeOrder", () => { |
| 295 | const OS = Option.makeOrder(Str.Order) |
| 296 | strictEqual(OS(Option.none(), Option.none()), 0) |
| 297 | strictEqual(OS(Option.some("a"), Option.none()), 1) |
| 298 | strictEqual(OS(Option.none(), Option.some("a")), -1) |
| 299 | strictEqual(OS(Option.some("a"), Option.some("a")), 0) |
| 300 | strictEqual(OS(Option.some("a"), Option.some("b")), -1) |
| 301 | strictEqual(OS(Option.some("b"), Option.some("a")), 1) |
| 302 | }) |
| 303 | |
| 304 | it("flatMapNullishOr", () => { |
| 305 | interface X { |
| 306 | readonly a?: { |
| 307 | readonly b?: { |
| 308 | readonly c?: { |
| 309 | readonly d: number |
| 310 | } |
| 311 | } |
| 312 | } |
| 313 | } |
| 314 | const x1: X = { a: {} } |
| 315 | const x2: X = { a: { b: {} } } |
| 316 | const x3: X = { a: { b: { c: { d: 1 } } } } |
| 317 | assertNone( |
| 318 | pipe( |
| 319 | Option.fromNullishOr(x1.a), |
| 320 | Option.flatMapNullishOr((x) => x.b), |
| 321 | Option.flatMapNullishOr((x) => x.c), |
| 322 | Option.flatMapNullishOr((x) => x.d) |
| 323 | ) |
| 324 | ) |
| 325 | assertNone( |
| 326 | pipe( |
| 327 | Option.fromNullishOr(x2.a), |
| 328 | Option.flatMapNullishOr((x) => x.b), |
| 329 | Option.flatMapNullishOr((x) => x.c), |
| 330 | Option.flatMapNullishOr((x) => x.d) |
| 331 | ) |
| 332 | ) |
| 333 | assertSome( |
| 334 | pipe( |
| 335 | Option.fromNullishOr(x3.a), |
| 336 | Option.flatMapNullishOr((x) => x.b), |
| 337 | Option.flatMapNullishOr((x) => x.c), |
| 338 | Option.flatMapNullishOr((x) => x.d) |
| 339 | ), |
| 340 | 1 |
| 341 | ) |
| 342 | }) |
| 343 | |
| 344 | it("fromNullishOr", () => { |
| 345 | assertSome(Option.fromNullishOr(2), 2) |
| 346 | assertNone(Option.fromNullishOr(null)) |
| 347 | assertNone(Option.fromNullishOr(undefined)) |
| 348 | }) |
| 349 | |
| 350 | it("liftPredicate", () => { |
| 351 | assertNone(pipe(1, Option.liftPredicate(gt2))) |
| 352 | assertSome(pipe(3, Option.liftPredicate(gt2)), 3) |
| 353 | assertNone(Option.liftPredicate(1, gt2)) |
| 354 | assertSome(Option.liftPredicate(3, gt2), 3) |
| 355 | |
| 356 | type Direction = "asc" | "desc" |
| 357 | const isDirection = (s: string): s is Direction => s === "asc" || s === "desc" |
| 358 | assertSome(pipe("asc", Option.liftPredicate(isDirection)), "asc") |
| 359 | assertNone(pipe("foo", Option.liftPredicate(isDirection))) |
| 360 | assertSome(Option.liftPredicate("asc", isDirection), "asc") |
| 361 | assertNone(Option.liftPredicate("foo", isDirection)) |
| 362 | }) |
| 363 | |
| 364 | it("containsWith", () => { |
| 365 | const containsWith = Option.containsWith<number>((self, that) => self % 2 === that % 2) |
| 366 | assertTrue(pipe(Option.some(2), containsWith(2))) |
| 367 | assertTrue(pipe(Option.some(4), containsWith(4))) |
| 368 | assertTrue(pipe(Option.some(1), containsWith(3))) |
| 369 | |
| 370 | assertFalse(pipe(Option.none(), containsWith(2))) |
| 371 | assertFalse(pipe(Option.some(2), containsWith(1))) |
| 372 | }) |
| 373 | |
| 374 | it("contains", () => { |
| 375 | assertFalse(pipe(Option.none(), Option.contains(2))) |
| 376 | assertTrue(pipe(Option.some(2), Option.contains(2))) |
| 377 | assertFalse(pipe(Option.some(2), Option.contains(1))) |
| 378 | }) |
| 379 | |
| 380 | it("isNone", () => { |
| 381 | assertTrue(Option.isNone(Option.none())) |
| 382 | assertFalse(Option.isNone(Option.some(1))) |
| 383 | }) |
| 384 | |
| 385 | it("isSome", () => { |
| 386 | assertFalse(Option.isSome(Option.none())) |
| 387 | assertTrue(Option.isSome(Option.some(1))) |
| 388 | }) |
| 389 | |
| 390 | it("exists", () => { |
| 391 | const predicate = (a: number) => a === 2 |
| 392 | assertFalse(pipe(Option.none(), Option.exists(predicate))) |
| 393 | assertFalse(pipe(Option.some(1), Option.exists(predicate))) |
| 394 | assertTrue(pipe(Option.some(2), Option.exists(predicate))) |
| 395 | }) |
| 396 | |
| 397 | it("liftNullishOr", () => { |
| 398 | const f = Option.liftNullishOr((n: number) => (n > 0 ? n : null)) |
| 399 | assertSome(f(1), 1) |
| 400 | assertNone(f(-1)) |
| 401 | }) |
| 402 | |
| 403 | it("liftThrowable", () => { |
| 404 | const parse = Option.liftThrowable(JSON.parse) |
| 405 | assertSome(parse("1"), 1) |
| 406 | assertNone(parse("")) |
| 407 | }) |
| 408 | |
| 409 | it("tap", () => { |
| 410 | assertNone(Option.tap(Option.none(), () => Option.none())) |
| 411 | assertNone(Option.tap(Option.some(1), () => Option.none())) |
| 412 | assertNone(Option.tap(Option.none(), (n) => Option.some(n * 2))) |
| 413 | assertSome(Option.tap(Option.some(1), (n) => Option.some(n * 2)), 1) |
| 414 | }) |
| 415 | |
| 416 | it("filter keeps Do notation records only when the predicate passes", () => { |
| 417 | assertSome( |
| 418 | pipe( |
| 419 | Option.Do, |
| 420 | Option.bind("x", () => Option.some("a")), |
| 421 | Option.bind("y", () => Option.some("a")), |
| 422 | Option.filter(({ x, y }) => x === y) |
| 423 | ), |
| 424 | { x: "a", y: "a" } |
| 425 | ) |
| 426 | assertNone( |
| 427 | pipe( |
| 428 | Option.Do, |
| 429 | Option.bind("x", () => Option.some("a")), |
| 430 | Option.bind("y", () => Option.some("b")), |
| 431 | Option.filter(({ x, y }) => x === y) |
| 432 | ) |
| 433 | ) |
| 434 | }) |
| 435 | |
| 436 | it("zipWith", () => { |
| 437 | assertNone(pipe(Option.none(), Option.zipWith(Option.some(2), (a, b) => a + b))) |
| 438 | assertNone(pipe(Option.some(1), Option.zipWith(Option.none(), (a, b) => a + b))) |
| 439 | assertSome(pipe(Option.some(1), Option.zipWith(Option.some(2), (a, b) => a + b)), 3) |
| 440 | }) |
| 441 | |
| 442 | it("reduceCompact", () => { |
| 443 | const sumCompact = Option.reduceCompact(0, Num.sum) |
| 444 | strictEqual(sumCompact([]), 0) |
| 445 | strictEqual(sumCompact([Option.some(2), Option.some(3)]), 5) |
| 446 | strictEqual(sumCompact([Option.some(2), Option.none(), Option.some(3)]), 5) |
| 447 | }) |
| 448 | |
| 449 | it("makeEquivalence", () => { |
| 450 | const isEquivalent = Option.makeEquivalence(Equivalence.strictEqual<number>()) |
| 451 | assertTrue(isEquivalent(Option.none(), Option.none())) |
| 452 | assertFalse(isEquivalent(Option.none(), Option.some(1))) |
| 453 | assertFalse(isEquivalent(Option.some(1), Option.none())) |
| 454 | assertFalse(isEquivalent(Option.some(2), Option.some(1))) |
| 455 | assertFalse(isEquivalent(Option.some(1), Option.some(2))) |
| 456 | assertTrue(isEquivalent(Option.some(2), Option.some(2))) |
| 457 | }) |
| 458 | |
| 459 | describe("all", () => { |
| 460 | it("tuple", () => { |
| 461 | assertSome(Option.all([]), []) |
| 462 | assertSome(Option.all([Option.some(1), Option.some("hello")]), [1, "hello"]) |
| 463 | assertNone(Option.all([Option.some(1), Option.none()])) |
| 464 | }) |
| 465 | |
| 466 | it("iterable", () => { |
| 467 | assertSome(Option.all([]), []) |
| 468 | assertNone(Option.all([Option.none()])) |
| 469 | assertSome(Option.all([Option.some(1), Option.some(2)]), [1, 2]) |
| 470 | assertSome(Option.all(new Set([Option.some(1), Option.some(2)])), [1, 2]) |
| 471 | assertNone(Option.all([Option.some(1), Option.none()])) |
| 472 | }) |
| 473 | |
| 474 | it("struct", () => { |
| 475 | assertSome( |
| 476 | Option.all({ a: Option.some(1), b: Option.some("hello") }), |
| 477 | { a: 1, b: "hello" } |
| 478 | ) |
| 479 | assertNone(Option.all({ a: Option.some(1), b: Option.none() })) |
| 480 | }) |
| 481 | }) |
| 482 | |
| 483 | it(".pipe()", () => { |
| 484 | assertSome(Option.some(1).pipe(Option.map((n) => n + 1)), 2) |
| 485 | }) |
| 486 | |
| 487 | it("lift2", () => { |
| 488 | const f = Option.lift2((a: number, b: number): number => a + b) |
| 489 | assertNone(f(Option.none(), Option.none())) |
| 490 | assertNone(f(Option.some(1), Option.none())) |
| 491 | assertNone(f(Option.none(), Option.some(2))) |
| 492 | assertSome(f(Option.some(1), Option.some(2)), 3) |
| 493 | }) |
| 494 | |
| 495 | describe("do notation", () => { |
| 496 | it("Do", () => { |
| 497 | assertSome(Option.Do, {}) |
| 498 | }) |
| 499 | |
| 500 | it("bindTo", () => { |
| 501 | assertSome(pipe(Option.some(1), Option.bindTo("a")), { a: 1 }) |
| 502 | assertNone(pipe(Option.none(), Option.bindTo("a"))) |
| 503 | assertSome( |
| 504 | pipe( |
| 505 | Option.some(1), |
| 506 | Option.bindTo("__proto__"), |
| 507 | Option.bind("x", () => Option.some(2)) |
| 508 | ), |
| 509 | { x: 2, ["__proto__"]: 1 } |
| 510 | ) |
| 511 | }) |
| 512 | |
| 513 | it("bind", () => { |
| 514 | assertSome(pipe(Option.some(1), Option.bindTo("a"), Option.bind("b", ({ a }) => Option.some(a + 1))), { |
| 515 | a: 1, |
| 516 | b: 2 |
| 517 | }) |
| 518 | assertNone( |
| 519 | pipe(Option.some(1), Option.bindTo("a"), Option.bind("b", () => Option.none())) |
| 520 | ) |
| 521 | assertNone( |
| 522 | pipe(Option.none(), Option.bindTo("a"), Option.bind("b", () => Option.some(2))) |
| 523 | ) |
| 524 | assertSome( |
| 525 | pipe( |
| 526 | Option.some(1), |
| 527 | Option.bindTo("a"), |
| 528 | Option.bind("__proto__", ({ a }) => Option.some(a + 1)), |
| 529 | Option.bind("b", ({ a }) => Option.some(a + 1)) |
| 530 | ), |
| 531 | { a: 1, b: 2, ["__proto__"]: 2 } |
| 532 | ) |
| 533 | }) |
| 534 | |
| 535 | it("let", () => { |
| 536 | assertSome(pipe(Option.some(1), Option.bindTo("a"), Option.let("b", ({ a }) => a + 1)), { a: 1, b: 2 }) |
| 537 | assertNone( |
| 538 | pipe(Option.none(), Option.bindTo("a"), Option.let("b", () => 2)) |
| 539 | ) |
| 540 | assertSome( |
| 541 | pipe( |
| 542 | Option.some(1), |
| 543 | Option.bindTo("a"), |
| 544 | Option.let("__proto__", ({ a }) => a + 1), |
| 545 | Option.let("b", ({ a }) => a + 1) |
| 546 | ), |
| 547 | { a: 1, b: 2, ["__proto__"]: 2 } |
| 548 | ) |
| 549 | }) |
| 550 | }) |
| 551 | |
| 552 | it("as", () => { |
| 553 | assertNone(Option.none().pipe(Option.as("a"))) |
| 554 | assertSome(Option.some(1).pipe(Option.as("a")), "a") |
| 555 | |
| 556 | assertNone(Option.as(Option.none(), "a")) |
| 557 | assertSome(Option.as(Option.some(1), "a"), "a") |
| 558 | }) |
| 559 | |
| 560 | it("asVoid", () => { |
| 561 | assertNone(Option.none().pipe(Option.asVoid)) |
| 562 | assertSome(Option.some(1).pipe(Option.asVoid), undefined) |
| 563 | }) |
| 564 | }) |