rstypes 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/option.d.ts +135 -0
- package/lib/option.js +80 -0
- package/lib/result.d.ts +148 -0
- package/lib/result.js +85 -0
- package/package.json +39 -0
- package/readme.md +218 -0
package/lib/option.d.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Err, Ok, Result } from "./result";
|
|
2
|
+
/**
|
|
3
|
+
* An optional value.
|
|
4
|
+
*
|
|
5
|
+
* ### Example
|
|
6
|
+
* ```ts
|
|
7
|
+
* function divide(numerator: number, denominator: number): Option<number> {
|
|
8
|
+
* if(y === 0) {
|
|
9
|
+
* return None;
|
|
10
|
+
* } else {
|
|
11
|
+
* return Some(numerator / denominator);
|
|
12
|
+
* }
|
|
13
|
+
* }
|
|
14
|
+
*
|
|
15
|
+
* let result = divide(2.0, 3.0);
|
|
16
|
+
* result.match({
|
|
17
|
+
* Some(n) { console.log(`Result: ${x}`); },
|
|
18
|
+
* None() { console.log("Cannot divide by 0")}
|
|
19
|
+
* })
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export type Option<T> = Some<T> | None<T>;
|
|
23
|
+
/**
|
|
24
|
+
* Wrap a function that can return `undefined`, `null` or `NaN`, to instead return an Option,
|
|
25
|
+
* which will be None in the above cases.
|
|
26
|
+
*/
|
|
27
|
+
export declare function as_option<A extends any[], T>(fn: (...args: A) => T): (...args: A) => Option<T>;
|
|
28
|
+
/**
|
|
29
|
+
* Common method signatures for Option types.
|
|
30
|
+
*/
|
|
31
|
+
interface OptionMethods<T> {
|
|
32
|
+
/**
|
|
33
|
+
* Returns `other` if the result is `Some`, or `None` otherwise.
|
|
34
|
+
* @param other Option to return if the option is `Some`
|
|
35
|
+
*/
|
|
36
|
+
and<U>(other: Option<U>): Option<U>;
|
|
37
|
+
/**
|
|
38
|
+
* Return the contained `Some` value. Will throw a hard error with the specified message if called on `None`.
|
|
39
|
+
* @param message Custom message to throw if `None`
|
|
40
|
+
*/
|
|
41
|
+
expect(message: string): T;
|
|
42
|
+
/**
|
|
43
|
+
* returns `true` if the option is `Some`, `false` if `None`.
|
|
44
|
+
*/
|
|
45
|
+
is_some(): this is Some<T>;
|
|
46
|
+
/**
|
|
47
|
+
* returns `true` if the option is `Some` and the value satisfies
|
|
48
|
+
* the given predicate, `false` otherwise.
|
|
49
|
+
*/
|
|
50
|
+
is_some_and(predicate: (value: T) => boolean): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* returns `false` if the option is `Some`, `true` if `None`.
|
|
53
|
+
*/
|
|
54
|
+
is_none(): this is None<T>;
|
|
55
|
+
/**
|
|
56
|
+
* Returns `true` if the option is `None` or the value satisfies
|
|
57
|
+
* the given predicate, `false` otherwise
|
|
58
|
+
*/
|
|
59
|
+
is_none_or(predicate: (value: T) => boolean): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Maps an `Option<T>` into an `Option<U>`.
|
|
62
|
+
* Will return `Some(fn(value))` if the option is `Some`, or propagate `None`.
|
|
63
|
+
* @param fn Function to transform the inner `value`
|
|
64
|
+
*/
|
|
65
|
+
map<U>(fn: (arg: T) => U): Option<U>;
|
|
66
|
+
/**
|
|
67
|
+
* Pattern-match on the option, running the `Some` or `None` function in the respective cases.
|
|
68
|
+
* @param matcher Object containing a `Some(value)` and `None(error)` function to run
|
|
69
|
+
*/
|
|
70
|
+
match<R>(matcher: {
|
|
71
|
+
Some(value: T): R;
|
|
72
|
+
None(): R;
|
|
73
|
+
}): R;
|
|
74
|
+
/**
|
|
75
|
+
* Pattern-match on the option, running the former or latter function
|
|
76
|
+
* in the `Some` or `None` cases respectively.
|
|
77
|
+
* @param on_some Function to run if the option is `Some`
|
|
78
|
+
* @param on_none Function to run if the option is `None`
|
|
79
|
+
*/
|
|
80
|
+
match<R>(on_some: (value: T) => R, on_none: () => R): R;
|
|
81
|
+
/**
|
|
82
|
+
* Transforms the Option into a Result, mapping Some(value) to Ok(value)
|
|
83
|
+
* and None to Err(error).
|
|
84
|
+
* @param error Err value to use if the option is `None`
|
|
85
|
+
*/
|
|
86
|
+
ok_or<E>(error: E): Result<T, E>;
|
|
87
|
+
/**
|
|
88
|
+
* Returns `other` if the result is `None`, otherwise returns the first option's `Some` value.
|
|
89
|
+
* @param other Option to return if the option is `Some`
|
|
90
|
+
*/
|
|
91
|
+
or(other: Option<T>): Option<T>;
|
|
92
|
+
/**
|
|
93
|
+
* Return the contained `Some` value. Will throw a hard error if called on `None`.
|
|
94
|
+
*/
|
|
95
|
+
unwrap(): T;
|
|
96
|
+
/**
|
|
97
|
+
* Return the contained `Some` value, or `default_value` if called on `None`.
|
|
98
|
+
* @param default_value Value to use if the option is `None`
|
|
99
|
+
*/
|
|
100
|
+
unwrap_or(default_value: T): T;
|
|
101
|
+
/**
|
|
102
|
+
* Return the contained `Some` value, or the result of `otherwise` if called on `None`.
|
|
103
|
+
* @param otherwise Function to run if the option is `None`
|
|
104
|
+
*/
|
|
105
|
+
unwrap_or_else(otherwise: () => T): T;
|
|
106
|
+
/**
|
|
107
|
+
* Returns None if either both the option and `other` are `Some` or `None`,
|
|
108
|
+
* or the single `Some(value)` otherwise.
|
|
109
|
+
*/
|
|
110
|
+
xor(other: Option<T>): Option<T>;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* A present optional value.
|
|
114
|
+
*/
|
|
115
|
+
export interface Some<T> extends OptionMethods<T> {
|
|
116
|
+
map<U>(fn: (value: T) => U): Some<U>;
|
|
117
|
+
ok_or<E>(error: E): Ok<T, E>;
|
|
118
|
+
or(other: Option<T>): Some<T>;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* No optional value.
|
|
122
|
+
*/
|
|
123
|
+
export interface None<T = any> extends OptionMethods<T> {
|
|
124
|
+
and<U>(other: Option<U>): None<U>;
|
|
125
|
+
expect(message: string): never;
|
|
126
|
+
map<U>(fn: (value: T) => U): None<U>;
|
|
127
|
+
ok_or<E>(error: E): Err<T, E>;
|
|
128
|
+
unwrap(): never;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* @param value Inner value
|
|
132
|
+
*/
|
|
133
|
+
export declare const Some: <T>(value: T) => Some<T>;
|
|
134
|
+
export declare const None: None;
|
|
135
|
+
export {};
|
package/lib/option.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.None = exports.Some = void 0;
|
|
4
|
+
exports.as_option = as_option;
|
|
5
|
+
const result_1 = require("./result");
|
|
6
|
+
/**
|
|
7
|
+
* Wrap a function that can return `undefined`, `null` or `NaN`, to instead return an Option,
|
|
8
|
+
* which will be None in the above cases.
|
|
9
|
+
*/
|
|
10
|
+
function as_option(fn) {
|
|
11
|
+
return (...args) => {
|
|
12
|
+
let out = fn(...args);
|
|
13
|
+
if ((typeof out === "number" && isNaN(out))
|
|
14
|
+
|| out === null
|
|
15
|
+
|| out === "undefined") {
|
|
16
|
+
return exports.None;
|
|
17
|
+
}
|
|
18
|
+
return (0, exports.Some)(out);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
// Implementations
|
|
22
|
+
const nodeInspect = Symbol.for('nodejs.util.inspect.custom');
|
|
23
|
+
/**
|
|
24
|
+
* @param value Inner value
|
|
25
|
+
*/
|
|
26
|
+
const Some = (value) => ({
|
|
27
|
+
and(other) { return other; },
|
|
28
|
+
expect() { return value; },
|
|
29
|
+
is_some() { return true; },
|
|
30
|
+
is_some_and(predicate) { return predicate(value); },
|
|
31
|
+
is_none() { return false; },
|
|
32
|
+
is_none_or(predicate) { return predicate(value); },
|
|
33
|
+
map(fn) { return (0, exports.Some)(fn(value)); },
|
|
34
|
+
match(matcher_or_some) {
|
|
35
|
+
if ("Some" in matcher_or_some) {
|
|
36
|
+
return matcher_or_some.Some(value);
|
|
37
|
+
}
|
|
38
|
+
return matcher_or_some(value);
|
|
39
|
+
},
|
|
40
|
+
ok_or() { return (0, result_1.Ok)(value); },
|
|
41
|
+
or() { return this; },
|
|
42
|
+
unwrap() { return value; },
|
|
43
|
+
unwrap_or() { return value; },
|
|
44
|
+
unwrap_or_else(_) { return value; },
|
|
45
|
+
xor(other) { return other.is_none() ? this : exports.None; },
|
|
46
|
+
toString() { return `Some(${value})`; },
|
|
47
|
+
[nodeInspect](_depth, inspectOptions, inspect) {
|
|
48
|
+
const cyan = inspectOptions.colors ? `\x1b[${inspect.colors.cyan[0]}m` : "";
|
|
49
|
+
const reset = inspectOptions.colors ? `\x1b[${inspect.colors.reset[0]}m` : "";
|
|
50
|
+
return `${cyan}Some${reset}(${inspect(value, inspectOptions)})`;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
exports.Some = Some;
|
|
54
|
+
exports.None = Object.freeze({
|
|
55
|
+
and() { return this; },
|
|
56
|
+
expect(message) { throw Error(message); },
|
|
57
|
+
is_some() { return false; },
|
|
58
|
+
is_some_and() { return false; },
|
|
59
|
+
is_none() { return true; },
|
|
60
|
+
is_none_or() { return true; },
|
|
61
|
+
map() { return exports.None; },
|
|
62
|
+
match(matcher_or_some, none) {
|
|
63
|
+
if ("None" in matcher_or_some) {
|
|
64
|
+
return matcher_or_some.None();
|
|
65
|
+
}
|
|
66
|
+
return none();
|
|
67
|
+
},
|
|
68
|
+
ok_or(err) { return (0, result_1.Err)(err); },
|
|
69
|
+
or(other) { return other; },
|
|
70
|
+
unwrap() { throw Error("Called unwrap on a None value"); },
|
|
71
|
+
unwrap_or(default_value) { return default_value; },
|
|
72
|
+
unwrap_or_else(otherwise) { return otherwise(); },
|
|
73
|
+
xor(other) { return other.is_some() ? other : exports.None; },
|
|
74
|
+
toString() { return "None"; },
|
|
75
|
+
[nodeInspect](_depth, inspectOptions, inspect) {
|
|
76
|
+
const yellow = inspectOptions.colors ? `\x1b[${inspect.colors.yellow[0]}m` : "";
|
|
77
|
+
const reset = inspectOptions.colors ? `\x1b[${inspect.colors.reset[0]}m` : "";
|
|
78
|
+
return `${yellow}None${reset}`;
|
|
79
|
+
}
|
|
80
|
+
});
|
package/lib/result.d.ts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { None, Option, Some } from "./option";
|
|
2
|
+
/**
|
|
3
|
+
* A succesful value or an expected, recoverable error.
|
|
4
|
+
*
|
|
5
|
+
* ### Example
|
|
6
|
+
* ```ts
|
|
7
|
+
* function parse_int(n: string): Result<number, string> {
|
|
8
|
+
* let maybe = parseInt(n);
|
|
9
|
+
* if(isNaN(maybe)) return Err("Could not parse input");
|
|
10
|
+
* return Ok(maybe);
|
|
11
|
+
* }
|
|
12
|
+
*
|
|
13
|
+
* let result = parse_int(3);
|
|
14
|
+
* result.match({
|
|
15
|
+
* Ok(value) { console.log("Parsed number: ", value) },
|
|
16
|
+
* Err(error) { console.error("Error: ", error)}
|
|
17
|
+
* });
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export type Result<T, E> = Ok<T, E> | Err<T, E>;
|
|
21
|
+
/**
|
|
22
|
+
* Wrap a function that can throw, returning `Ok(fn())` if the function returns, `Err(error)` if it throws `error`.
|
|
23
|
+
* @param fn function that potentially throws
|
|
24
|
+
*/
|
|
25
|
+
export declare function as_result<A extends any[], T>(fn: (...args: A) => T): (...args: A) => Result<T, any>;
|
|
26
|
+
/**
|
|
27
|
+
* Common method signatures for Result types.
|
|
28
|
+
*/
|
|
29
|
+
interface ResultMethods<T, E> {
|
|
30
|
+
/**
|
|
31
|
+
* Returns `other` if the result is `Ok`, otherwise returns the first result's Err value.
|
|
32
|
+
* @param other result to return if the result is `Ok`.
|
|
33
|
+
*/
|
|
34
|
+
and<U>(other: Result<U, E>): Result<U, E>;
|
|
35
|
+
/**
|
|
36
|
+
* Transforms `Result<T, E>` into an `Option<E>`, mapping `Ok(value)` to `None` discarding `value`
|
|
37
|
+
* and `Err(error)` to `Some(error)`.
|
|
38
|
+
*/
|
|
39
|
+
err(): Option<E>;
|
|
40
|
+
/**
|
|
41
|
+
* Return the contained `Ok` value. Will throw a hard error with the specified message if called on `Err`.
|
|
42
|
+
* @param message custom message to throw if `None`
|
|
43
|
+
*/
|
|
44
|
+
expect(message: string): T;
|
|
45
|
+
/**
|
|
46
|
+
* returns `true` if the result is `Ok`, `false` if `Err`.
|
|
47
|
+
*/
|
|
48
|
+
is_ok(): this is Ok<T, E>;
|
|
49
|
+
/**
|
|
50
|
+
* returns `true` if the result is `Ok` and the value satisfies
|
|
51
|
+
* the given predicate, `false` otherwise.
|
|
52
|
+
*/
|
|
53
|
+
is_ok_and(predicate: (value: T) => boolean): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* returns `false` if the result is `Ok`, `true` if `Err`.
|
|
56
|
+
*/
|
|
57
|
+
is_err(): this is Err<T, E>;
|
|
58
|
+
/**
|
|
59
|
+
* returns `true` if the result is `Err` and the error satisfies
|
|
60
|
+
* the given predicate, `false` otherwise.
|
|
61
|
+
*/
|
|
62
|
+
is_err_and(predicate: (error: E) => boolean): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Maps a `Result<T, E>` into a `Result<U, E>`.
|
|
65
|
+
* Will return `Ok(fn(value))` if the result is `Ok`, or propagate `Err`.
|
|
66
|
+
* @param fn function to transform the inner value
|
|
67
|
+
*/
|
|
68
|
+
map<U>(fn: (value: T) => U): Result<U, E>;
|
|
69
|
+
/**
|
|
70
|
+
* Maps a `Result<T, E>` into a `Result<T, F>`.
|
|
71
|
+
* Will propagate `Ok`, or return `Err(fn(error))` if the result is `Err`.
|
|
72
|
+
* @param fn function to transform the inner value
|
|
73
|
+
*/
|
|
74
|
+
map_err<F>(fn: (error: E) => F): Result<T, F>;
|
|
75
|
+
/**
|
|
76
|
+
* Pattern-match on the result, running the `Ok` or `Err` function in the respective cases.
|
|
77
|
+
* @param matcher object containing an `Ok(value)` and `Err(error)` function to run
|
|
78
|
+
*/
|
|
79
|
+
match<R>(matcher: {
|
|
80
|
+
Ok(value: T): R;
|
|
81
|
+
Err(error: E): R;
|
|
82
|
+
}): R;
|
|
83
|
+
/**
|
|
84
|
+
* Pattern-match on the result, running the former or latter function
|
|
85
|
+
* in the `Ok` and `Err` cases respectively.
|
|
86
|
+
*
|
|
87
|
+
* @param on_ok function to run if the result is `Ok(value)`
|
|
88
|
+
* @param on_err function to run if the option is `Err(error)`
|
|
89
|
+
*/
|
|
90
|
+
match<R>(on_ok: (value: T) => R, on_err: (error: E) => R): R;
|
|
91
|
+
/**
|
|
92
|
+
* Transforms the `Result<T, E>` into an `Option<T>`, mapping `Ok(value)` to `Some(value)`
|
|
93
|
+
* and `Err(error)` to `None`, discarding `error`.
|
|
94
|
+
*/
|
|
95
|
+
ok(): Option<T>;
|
|
96
|
+
/**
|
|
97
|
+
* Returns `other` if the result is `Err`, otherwise returns the first result's `Ok` value.
|
|
98
|
+
* @param other result to return if the result is `Err`.
|
|
99
|
+
*/
|
|
100
|
+
or<F>(other: Result<T, F>): Result<T, F>;
|
|
101
|
+
/**
|
|
102
|
+
* Return the contained `Ok` value. Will throw a hard error if called on `Err`.
|
|
103
|
+
*/
|
|
104
|
+
unwrap(): T;
|
|
105
|
+
/**
|
|
106
|
+
* Return the contained `Ok` value, or `default_value` if called on `Err`.
|
|
107
|
+
* @param default_value value to use if the result is `Err`
|
|
108
|
+
*/
|
|
109
|
+
unwrap_or<T>(default_value: T): T;
|
|
110
|
+
/**
|
|
111
|
+
* Return the contained `Ok` value, or the result of `otherwise` if called on `Err`.
|
|
112
|
+
* @param otherwise function to use if the result is `Err`
|
|
113
|
+
*/
|
|
114
|
+
unwrap_or_else<T>(otherwise: (error: E) => T): T;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* A succesful result.
|
|
118
|
+
*/
|
|
119
|
+
export interface Ok<T, E> extends ResultMethods<T, E> {
|
|
120
|
+
err(): None<E>;
|
|
121
|
+
is_err_and(predicate: (error: E) => boolean): false;
|
|
122
|
+
map<U>(fn: (value: T) => U): Ok<U, E>;
|
|
123
|
+
map_err<F>(fn: (error: E) => F): Ok<T, F>;
|
|
124
|
+
ok(): Some<T>;
|
|
125
|
+
or<F>(other: Result<T, F>): Ok<T, F>;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* An expected, recoverable error.
|
|
129
|
+
*/
|
|
130
|
+
export interface Err<T, E> extends ResultMethods<T, E> {
|
|
131
|
+
and<U>(other: Result<U, E>): Err<U, E>;
|
|
132
|
+
err(): Some<E>;
|
|
133
|
+
expect(message: string): never;
|
|
134
|
+
is_ok_and(predicate: (value: T) => boolean): false;
|
|
135
|
+
map<U>(fn: (value: T) => U): Err<U, E>;
|
|
136
|
+
map_err<F>(fn: (error: E) => F): Err<T, F>;
|
|
137
|
+
ok(): None<T>;
|
|
138
|
+
unwrap(): never;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* @param value Inner value
|
|
142
|
+
*/
|
|
143
|
+
export declare const Ok: <T, E>(value: T) => Ok<T, E>;
|
|
144
|
+
/**
|
|
145
|
+
* @param error Inner error
|
|
146
|
+
*/
|
|
147
|
+
export declare const Err: <T, E>(error: E) => Err<T, E>;
|
|
148
|
+
export {};
|
package/lib/result.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Err = exports.Ok = void 0;
|
|
4
|
+
exports.as_result = as_result;
|
|
5
|
+
const option_1 = require("./option");
|
|
6
|
+
/**
|
|
7
|
+
* Wrap a function that can throw, returning `Ok(fn())` if the function returns, `Err(error)` if it throws `error`.
|
|
8
|
+
* @param fn function that potentially throws
|
|
9
|
+
*/
|
|
10
|
+
function as_result(fn) {
|
|
11
|
+
return (...args) => {
|
|
12
|
+
try {
|
|
13
|
+
return (0, exports.Ok)(fn(...args));
|
|
14
|
+
}
|
|
15
|
+
catch (x) {
|
|
16
|
+
return (0, exports.Err)(x);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
// Implementations
|
|
21
|
+
const nodeInspect = Symbol.for('nodejs.util.inspect.custom');
|
|
22
|
+
/**
|
|
23
|
+
* @param value Inner value
|
|
24
|
+
*/
|
|
25
|
+
const Ok = (value) => ({
|
|
26
|
+
and(other) { return other; },
|
|
27
|
+
err() { return option_1.None; },
|
|
28
|
+
expect() { return value; },
|
|
29
|
+
is_err() { return false; },
|
|
30
|
+
is_err_and() { return false; },
|
|
31
|
+
is_ok() { return true; },
|
|
32
|
+
is_ok_and(predicate) { return predicate(value); },
|
|
33
|
+
map(fn) { return (0, exports.Ok)(fn(value)); },
|
|
34
|
+
map_err() { return this; },
|
|
35
|
+
match(matcher_or_ok) {
|
|
36
|
+
if ("Ok" in matcher_or_ok) {
|
|
37
|
+
return matcher_or_ok.Ok(value);
|
|
38
|
+
}
|
|
39
|
+
return matcher_or_ok(value);
|
|
40
|
+
},
|
|
41
|
+
ok() { return (0, option_1.Some)(value); },
|
|
42
|
+
or() { return this; },
|
|
43
|
+
unwrap() { return value; },
|
|
44
|
+
unwrap_or() { return value; },
|
|
45
|
+
unwrap_or_else() { return value; },
|
|
46
|
+
toString() { return `Ok(${value})`; },
|
|
47
|
+
[nodeInspect](_depth, inspectOptions, inspect) {
|
|
48
|
+
const green = inspectOptions.colors ? `\x1b[${inspect.colors.green[0]}m` : "";
|
|
49
|
+
const reset = inspectOptions.colors ? `\x1b[${inspect.colors.reset[0]}m` : "";
|
|
50
|
+
return `${green}Ok${reset}(${inspect(value, inspectOptions)})`;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
exports.Ok = Ok;
|
|
54
|
+
/**
|
|
55
|
+
* @param error Inner error
|
|
56
|
+
*/
|
|
57
|
+
const Err = (error) => ({
|
|
58
|
+
and() { return this; },
|
|
59
|
+
err() { return (0, option_1.Some)(error); },
|
|
60
|
+
expect(message) { throw Error(`${message}: ${error}`); },
|
|
61
|
+
is_err() { return true; },
|
|
62
|
+
is_err_and(predicate) { return predicate(error); },
|
|
63
|
+
is_ok() { return false; },
|
|
64
|
+
is_ok_and() { return false; },
|
|
65
|
+
map() { return this; },
|
|
66
|
+
map_err(fn) { return (0, exports.Err)(fn(error)); },
|
|
67
|
+
match(matcher_or_ok, err) {
|
|
68
|
+
if ("Err" in matcher_or_ok) {
|
|
69
|
+
return matcher_or_ok.Err(error);
|
|
70
|
+
}
|
|
71
|
+
return err(error);
|
|
72
|
+
},
|
|
73
|
+
ok() { return option_1.None; },
|
|
74
|
+
or(other) { return other; },
|
|
75
|
+
unwrap() { throw Error(`Called unwrap on an Err value: ${error}`); },
|
|
76
|
+
unwrap_or(default_value) { return default_value; },
|
|
77
|
+
unwrap_or_else(otherwise) { return otherwise(error); },
|
|
78
|
+
toString() { return `Err(${error})`; },
|
|
79
|
+
[nodeInspect](_depth, inspectOptions, inspect) {
|
|
80
|
+
const red = inspectOptions.colors ? `\x1b[${inspect.colors.red[0]}m` : "";
|
|
81
|
+
const reset = inspectOptions.colors ? `\x1b[${inspect.colors.reset[0]}m` : "";
|
|
82
|
+
return `${red}Err${reset}(${inspect(error, inspectOptions)})`;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
exports.Err = Err;
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rstypes",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Type-safe implementation of Rust's Option and Result types",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/p2js/rstypes"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/p2js/rstypes",
|
|
10
|
+
"exports": {
|
|
11
|
+
"./option": {
|
|
12
|
+
"default": "./lib/option.js",
|
|
13
|
+
"types": "./lib/option.d.ts"
|
|
14
|
+
},
|
|
15
|
+
"./result": {
|
|
16
|
+
"default": "./lib/result.js",
|
|
17
|
+
"types": "./lib/result.d.ts"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"lib",
|
|
22
|
+
"readme.md"
|
|
23
|
+
],
|
|
24
|
+
"keywords": [
|
|
25
|
+
"type-safe",
|
|
26
|
+
"typescript",
|
|
27
|
+
"types",
|
|
28
|
+
"option",
|
|
29
|
+
"result",
|
|
30
|
+
"rust",
|
|
31
|
+
"rust-types",
|
|
32
|
+
"error-handling"
|
|
33
|
+
],
|
|
34
|
+
"author": "Alfio (https://github.com/p2js)",
|
|
35
|
+
"license": "ISC",
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc"
|
|
38
|
+
}
|
|
39
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# RSTypes
|
|
2
|
+
|
|
3
|
+
Type-safe implementation of lightweight Option and Result types in TypeScript (and JavaScript).
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { Option, Some, None } from "rstypes/option";
|
|
7
|
+
|
|
8
|
+
function index_in_array<T>(array: T[], search: T): Option<number> {
|
|
9
|
+
let index = array.indexOf(search);
|
|
10
|
+
|
|
11
|
+
if(index == -1) {
|
|
12
|
+
return None;
|
|
13
|
+
} else {
|
|
14
|
+
return Some(index);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let idx = index_in_array([1, 2, 3], 2);
|
|
19
|
+
|
|
20
|
+
idx.match({
|
|
21
|
+
Some(value) { console.log("Found at index: ", value) },
|
|
22
|
+
None() { console.log("Could not find in array") }
|
|
23
|
+
})
|
|
24
|
+
```
|
|
25
|
+
```ts
|
|
26
|
+
import { Result, Ok, Err } from "rstypes/result"; // ESM
|
|
27
|
+
const { Result, Ok, Err } = require("rstypes/result"); // CommonJS
|
|
28
|
+
|
|
29
|
+
function parse_int(str: string): Result<number, string> {
|
|
30
|
+
let maybe_number = parseInt(str);
|
|
31
|
+
if(isNaN(maybe_number)) return Err("Invalid string");
|
|
32
|
+
return Ok(maybe_number);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let result = parse_int("333");
|
|
36
|
+
result.match({
|
|
37
|
+
Ok(num) { console.log("Parsed number: ", num) },
|
|
38
|
+
Err(msg) { console.log("Error parsing: ", msg) }
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Table of contents
|
|
43
|
+
|
|
44
|
+
- [Usage](#usage)
|
|
45
|
+
- [Basic type information](#basic-type-information)
|
|
46
|
+
- [Handling Option and Result values](#handling-option-and-result-values)
|
|
47
|
+
- [match](#match)
|
|
48
|
+
- [unwrap, expect](#unwrap-expect)
|
|
49
|
+
- [unwrap_or, unwrap_or_else](#unwrap_or-unwrap_or_else)
|
|
50
|
+
- [is_ok, is_err](#is_ok-is_err)
|
|
51
|
+
- [is_ok_and, is_err_and](#is_ok_and-is_err_and)
|
|
52
|
+
- [map, map_err](#map-map_err)
|
|
53
|
+
- [ok, err](#ok-err)
|
|
54
|
+
- [and, or](#and-or)
|
|
55
|
+
- [Converting standard functions](#converting-standard-functions)
|
|
56
|
+
- [Developer considerations](#developer-considerations)
|
|
57
|
+
|
|
58
|
+
## Usage
|
|
59
|
+
|
|
60
|
+
You can install rstypes on [npm](https://npmjs.com/package/rstypes):
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
npm install rstypes
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Basic type information
|
|
67
|
+
|
|
68
|
+
Use `Option<T>` to represent an optional value, for example:
|
|
69
|
+
- An explicit optional parameter in a function.
|
|
70
|
+
- An object that may or may not have a value in its field, but should always have that field.
|
|
71
|
+
- A function that may or may not return a value.
|
|
72
|
+
|
|
73
|
+
Use `Result<T, E>` to represent either a successful return value or an error that is expected and recoverable, for example:
|
|
74
|
+
- A parsing function that can fail with malformed input.
|
|
75
|
+
- A function that can error based on external state.
|
|
76
|
+
- A function that can fail in multiple ways that should be handled separately.
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
These types give you a type-safe alternative to returning `null` or `undefined` and throwing exceptions, in ways that can be handled more explicitly and are immediately clear from the function signature.
|
|
80
|
+
|
|
81
|
+
### Handling Option and Result values
|
|
82
|
+
|
|
83
|
+
`Option` and `Result` values can be handled almost identically (The examples below will focus on `Result` but will clarify differences with handling `Option`s).
|
|
84
|
+
|
|
85
|
+
Consider an example `Result` variable:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
let res: Result<number, string> = /* ... */
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Where `number` is the `Ok` type and `string` is the `Err` type.
|
|
92
|
+
|
|
93
|
+
#### match
|
|
94
|
+
|
|
95
|
+
Matching on values is exhaustive, and can be done using a more verbose Rust-like syntax using an object, or simply two arrow functions for conciseness. This is considered the default way to handle values for its expressiveness and flexibility.
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
res.match({
|
|
99
|
+
Ok(value) { /* do something with value */ },
|
|
100
|
+
Err(error) { console.error(error); }
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
let x = res.match(
|
|
104
|
+
value => 2 * value,
|
|
105
|
+
error => { console.error("There was an error"); return 0; }
|
|
106
|
+
);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
> N.B. `Option` values match on the two functions `Some(value)` and `None()` instead, where the `None` case takes no arguments.
|
|
110
|
+
|
|
111
|
+
#### unwrap, expect
|
|
112
|
+
|
|
113
|
+
These two methods should **only** be used when you are sure that the value cannot be `Err`/`None` and just want immediate access to the inner value (ie. when the error case would violate a fundamental assumption of the program). These functions will hard error if called on `Err`/`None`, with `unwrap` throwing a generic error and `expect` throwing an error with the specified message:
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
let x: number = res.unwrap();
|
|
117
|
+
let y: number = res.expect("Should never error with the given inputs");
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### unwrap_or, unwrap_or_else
|
|
121
|
+
|
|
122
|
+
These two methods should be used when you don't care to handle the error case and want suitable default behaviour instead.
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
let x: number = res.unwrap_or(0);
|
|
126
|
+
let y: number = res.unwrap_or_else(() => Math.random()); // the function can also depend on the error value!
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### is_ok, is_err
|
|
130
|
+
|
|
131
|
+
These two methods return true or false when the `Result` value is the appropriate variant. They can be used for more traditional/non-exhaustive handling, and TypeScript will automatically narrow the type to the respective variant within their blocks.
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
if(res.is_ok()) {
|
|
135
|
+
let x = res.unwrap(); // Guaranteed not to fail
|
|
136
|
+
// ...
|
|
137
|
+
} else {
|
|
138
|
+
console.log("There was some error");
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
> N.B. `Option` values have analogous predicates `is_some` and `is_none`.
|
|
142
|
+
|
|
143
|
+
### is_ok_and, is_err_and
|
|
144
|
+
|
|
145
|
+
These two methods return true when the result value is of the appropriate variant and a given predicate evaluates to `true` with the inner value, returning false otherwise. They can be used to check for a given property of contained values.
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
if(res.is_ok_and(n => n % 2 == 0)) {
|
|
149
|
+
let even = res.unwrap();
|
|
150
|
+
//...
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
> N.B. `Option` values have equivalent `is_some_and` and `is_none_or`, with the latter returning true if the option is `None`, or `Some(value)` with the predicate being true for `value`.
|
|
154
|
+
|
|
155
|
+
#### map, map_err
|
|
156
|
+
|
|
157
|
+
These two methods can convert between `Result` values with a different `Ok` type and `Err` type respectively, propagating the alternate value otherwise. They can be used to perform transformations conditionally.
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
let s: Result<string, string> = res.map((n) => `number ${n}`);
|
|
161
|
+
let e: Result<number, Error> = res.map_err((e) => Error(e));
|
|
162
|
+
```
|
|
163
|
+
> N.B. `Option` values only have `map` to translate between `Option<T>` and `Option<U>`.
|
|
164
|
+
|
|
165
|
+
### ok, err
|
|
166
|
+
|
|
167
|
+
These two methods convert a `Result<T,E>` into an `Option<T>` and `Option<E>` respectively, returning `Some(value)` if the variant matches the method called and `None` otherwise. They can be used to translate between the two types as needed.
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
let n: Option<number> = res.ok();
|
|
171
|
+
let s: Option<number> = res.err();
|
|
172
|
+
```
|
|
173
|
+
> N.B. `Option` values have an equivalent `ok_or(error)` method to translate `Some(value)` into `Ok(value)` and `None` into `Err(error)`.
|
|
174
|
+
|
|
175
|
+
### and, or
|
|
176
|
+
|
|
177
|
+
These two methods can be used to perform logic on `Result` values, evaluating to the alternative given if the first result is `Ok` or `Err` respectively, or itself otherwise.
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
let res2: Result<number, string> = Err("something");
|
|
181
|
+
|
|
182
|
+
let or = res2.or(res) // or == res
|
|
183
|
+
let and = res2.and(res); // or == Err("something")
|
|
184
|
+
```
|
|
185
|
+
> N.B. `Option` values also have an `xor` method that performs similar logic, evaluating to `None` when both options are `Some` or `None` and the only `Some(value)` otherwise.
|
|
186
|
+
|
|
187
|
+
### Converting standard functions
|
|
188
|
+
|
|
189
|
+
This library also offers two wrappers to convert other JavaScript functions into these patterns:
|
|
190
|
+
|
|
191
|
+
- `as_result` takes a function that can throw and outputs a function that returns a `Result`, with `Ok` if it returned and `Err` if it threw.
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
import { as_result } from "rstypes/result";
|
|
195
|
+
let parse_json = as_result(JSON.parse);
|
|
196
|
+
|
|
197
|
+
let parsed: Result<any, SyntaxError> = parse_json("{}"); // parsed = {}
|
|
198
|
+
let error: Result<any, SyntaxError> = parse_json("abcd"); // error = Err(SyntaxError(...))
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
- `as_option` takes a function that can return `NaN`, `null` or `undefined` and outputs a function that returns an `Option`, with `None` if it returned one of those values or `Some` otherwise.
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
import { as_option } from "rstypes/option";
|
|
206
|
+
let sqrt = as_option(Math.sqrt);
|
|
207
|
+
|
|
208
|
+
let y1: Option<number> = sqrt(1); // y1 = Some(1)
|
|
209
|
+
let y2: Option<number> = sqrt(-1); // y2 = None
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Developer considerations
|
|
213
|
+
|
|
214
|
+
For additional type safety (such as not being able to call `unwrap` on directly instantiated `Err` values), the outputs of `Ok(x: T)` and `Err(e: E)` are not considered to be values of `Result<T, E>` but rather of their own individual types: `Ok<T, unknown>` and `Err<unknown, E>`. This is also due to the impossibility of inferring a `T` type from a construction of `Err<unknown, E>` and vice versa, resulting in confusing type signatures when returning both from functions.
|
|
215
|
+
|
|
216
|
+
Therefore, for best developer experience, take care to use explicit `Result<T, E>` type annotations where possible.
|
|
217
|
+
|
|
218
|
+
> N.B. Holds analogously for `Some` and `None`.
|