Skip to content

Commit f3590c7

Browse files
committed
Remove distinct union type and method
This removes the union type and interface for each type and uses just an interface. The `is___` methods no longer can discriminate between types fully; it only ensures that the instance is a single particular type. This simplifies the architecture of the library and makes documentation easier at the expense of not being able to be as exhaustive about the types in conditionals. For example, an `if/else` condition that checks for `isSome` no longer knows that the `else` condition contains a `None`. Related to #2
1 parent 96e06ce commit f3590c7

17 files changed

+229
-213
lines changed

CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this
66
project adheres to [Semantic Versioning](http://semver.org/).
77

8+
## Unreleased
9+
10+
### Changed
11+
12+
- Removed distinct `Option` union type and `OptionMethods` interface in favor of
13+
a single interface that `Option` and `None` implement
14+
- Removed distinct `Result` union type and `ResultMethods` interface in favor of
15+
a single interface that `Ok` and `Err` implement
16+
- `isSome`, `isNone`, `isOk` and `isErr` are now methods, rather than properties
17+
818
## 0.5.3 - 2022-03-17
919

1020
### Fixed

lib/option/methods.ts

-27
This file was deleted.

lib/option/none.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import { ExpectError, UnwrapError } from "../exceptions.ts";
44
import { Err } from "../result/err.ts";
55

66
Deno.test("#isSome", () => {
7-
assertEquals(None.isSome, false);
7+
assertEquals(None.isSome(), false);
88
});
99

1010
Deno.test("#isNone", () => {
11-
assertEquals(None.isNone, true);
11+
assertEquals(None.isNone(), true);
1212
});
1313

1414
Deno.test("#unwrap", () => {

lib/option/none.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
import { type OptionMethods } from "./methods.ts";
21
import { type Option } from "./option.ts";
2+
import { type Some } from "./some.ts";
33
import { Err, type Result } from "../result/mod.ts";
44
import { ExpectError, UnwrapError } from "../exceptions.ts";
55

6-
class NoneImpl implements OptionMethods<never> {
7-
readonly isSome = false;
6+
class NoneImpl implements Option<never> {
7+
isSome(): this is Some<never> {
8+
return false;
9+
}
810

9-
readonly isNone = true;
11+
isNone(): this is typeof None {
12+
return true;
13+
}
1014

1115
unwrap(): never {
1216
throw new UnwrapError("Cannot unwrap `None`");

lib/option/option.test.ts

+2-6
Original file line numberDiff line numberDiff line change
@@ -100,18 +100,14 @@ Deno.test("discriminating `Some` from `None`", async (t) => {
100100
const result = toOption(1);
101101

102102
await t.step("using `#isSome`", () => {
103-
if (result.isSome) {
103+
if (result.isSome()) {
104104
const _some: Some<number> = result;
105-
} else {
106-
const _none: typeof None = result;
107105
}
108106
});
109107

110108
await t.step("using `#isNone`", () => {
111-
if (result.isNone) {
109+
if (result.isNone()) {
112110
const _none: typeof None = result;
113-
} else {
114-
const _some: Some<number> = result;
115111
}
116112
});
117113
});

lib/option/option.ts

+25-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,28 @@
11
import { type Some } from "./some.ts";
22
import { type None } from "./none.ts";
3+
import { type Result } from "../result/mod.ts";
34

4-
export type Option<T> = Some<T> | typeof None;
5+
export interface Option<T> {
6+
isSome(): this is Some<T>;
7+
isNone(): this is typeof None;
8+
9+
unwrap(): T;
10+
unwrapOr(fallback: T): T;
11+
unwrapOrElse(fallback: () => T): T;
12+
13+
expect(message: string): T;
14+
15+
andThen<U>(op: (value: T) => Option<U>): Option<U>;
16+
17+
map<U>(op: (value: T) => U): Option<U>;
18+
mapOr<U>(fallback: U, op: (value: T) => U): U;
19+
mapOrElse<U>(fallback: () => U, op: (value: T) => U): U;
20+
21+
and(other: Option<T>): Option<T>;
22+
23+
or(other: Option<T>): Option<T>;
24+
orElse(other: () => Option<T>): Option<T>;
25+
26+
okOr<E>(err: E): Result<T, E>;
27+
okOrElse<E>(err: () => E): Result<T, E>;
28+
}

lib/option/some.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { Some } from "./some.ts";
33
import { Ok } from "../result/mod.ts";
44

55
Deno.test("#isSome", () => {
6-
assertEquals(new Some(1).isSome, true);
6+
assertEquals(new Some(1).isSome(), true);
77
});
88

99
Deno.test("#isNone", () => {
10-
assertEquals(new Some(1).isNone, false);
10+
assertEquals(new Some(1).isNone(), false);
1111
});
1212

1313
Deno.test("#unwrap", () => {

lib/option/some.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1-
import { type OptionMethods } from "./methods.ts";
21
import { type Option } from "./option.ts";
2+
import { type None } from "./none.ts";
33
import { Ok, type Result } from "../result/mod.ts";
44

55
/**
66
* Contains the value of an `Option<T>`
77
*
88
* @template T The type of the present value
99
*/
10-
export class Some<T> implements OptionMethods<T> {
11-
readonly isSome = true;
10+
export class Some<T> implements Option<T> {
11+
isSome(): this is Some<T> {
12+
return true;
13+
}
1214

13-
readonly isNone = false;
15+
isNone(): this is typeof None {
16+
return false;
17+
}
1418

1519
constructor(private val: T) {}
1620

lib/result/aggregators.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ export function all<T, E>(results: Iterable<Result<T, E>>): Result<T[], E> {
1414
const collection = [];
1515

1616
for (const result of results) {
17-
if (result.isOk) {
17+
if (result.isOk()) {
1818
collection.push(result.unwrap());
19-
} else {
19+
}
20+
21+
if (result.isErr()) {
2022
return result;
2123
}
2224
}
@@ -36,9 +38,11 @@ export function any<T, E>(results: Iterable<Result<T, E>>): Result<T, E[]> {
3638
const collection = [];
3739

3840
for (const result of results) {
39-
if (result.isErr) {
41+
if (result.isErr()) {
4042
collection.push(result.unwrapErr());
41-
} else {
43+
}
44+
45+
if (result.isOk()) {
4246
return result;
4347
}
4448
}

lib/result/err.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ import { ExpectError, UnwrapError } from "../exceptions.ts";
1111
Deno.test("#isOk", () => {
1212
const err = new Err("whatever");
1313

14-
assertEquals(err.isOk, false);
14+
assertEquals(err.isOk(), false);
1515
});
1616

1717
Deno.test("#isErr", () => {
1818
const err = new Err("whatever");
1919

20-
assertEquals(err.isErr, true);
20+
assertEquals(err.isErr(), true);
2121
});
2222

2323
Deno.test("#unwrap", () => {

lib/result/err.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { type ResultMethods } from "./methods.ts";
21
import { type Result } from "./result.ts";
2+
import { type Ok } from "./ok.ts";
33
import { EventualResult } from "./eventual.ts";
44
import { None, type Option, Some } from "../option/mod.ts";
55
import { ExpectError, UnwrapError } from "../exceptions.ts";
@@ -9,12 +9,17 @@ import { ExpectError, UnwrapError } from "../exceptions.ts";
99
*
1010
* @template E The type of the error value
1111
*/
12-
export class Err<E> implements ResultMethods<never, E> {
13-
readonly isOk = false;
14-
readonly isErr = true;
15-
12+
export class Err<E> implements Result<never, E> {
1613
constructor(private val: E) {}
1714

15+
isOk(): this is Ok<never> {
16+
return false;
17+
}
18+
19+
isErr(): this is Err<E> {
20+
return true;
21+
}
22+
1823
unwrap(): never {
1924
throw new UnwrapError("Cannot unwrap `Err`", this.val);
2025
}

lib/result/eventual.test.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,9 @@ Deno.test("#unwrapOr", async () => {
270270

271271
assertEquals(await eventuallyOk.unwrapOr("other"), "ok");
272272

273-
const eventuallyErr = new EventualResult(Promise.reject("err"));
273+
const eventuallyErr: EventualResult<unknown, string> = new EventualResult(
274+
Promise.reject("err"),
275+
);
274276

275277
assertEquals(await eventuallyErr.unwrapOr("other"), "other");
276278
});

lib/result/methods.ts

-132
This file was deleted.

0 commit comments

Comments
 (0)