happy-rusty 1.5.0 → 1.6.1
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/CHANGELOG.md +214 -0
- package/LICENSE +21 -674
- package/README.cn.md +265 -19
- package/README.md +261 -21
- package/dist/main.cjs +382 -32
- package/dist/main.cjs.map +1 -1
- package/dist/main.mjs +374 -33
- package/dist/main.mjs.map +1 -1
- package/dist/types.d.ts +2002 -52
- package/package.json +38 -25
- package/dist/types.d.ts.map +0 -1
- package/docs/README.md +0 -47
- package/docs/functions/Err.md +0 -46
- package/docs/functions/Ok.md +0 -70
- package/docs/functions/Some.md +0 -45
- package/docs/functions/isOption.md +0 -35
- package/docs/functions/isResult.md +0 -36
- package/docs/functions/promiseToAsyncResult.md +0 -50
- package/docs/interfaces/None.md +0 -979
- package/docs/interfaces/Option.md +0 -857
- package/docs/interfaces/Result.md +0 -903
- package/docs/type-aliases/AsyncIOResult.md +0 -24
- package/docs/type-aliases/AsyncOption.md +0 -24
- package/docs/type-aliases/AsyncResult.md +0 -25
- package/docs/type-aliases/AsyncVoidIOResult.md +0 -17
- package/docs/type-aliases/AsyncVoidResult.md +0 -23
- package/docs/type-aliases/IOResult.md +0 -24
- package/docs/type-aliases/VoidIOResult.md +0 -17
- package/docs/type-aliases/VoidResult.md +0 -23
- package/docs/variables/None.md +0 -18
- package/docs/variables/RESULT_FALSE.md +0 -18
- package/docs/variables/RESULT_TRUE.md +0 -18
- package/docs/variables/RESULT_VOID.md +0 -17
- package/docs/variables/RESULT_ZERO.md +0 -18
- package/src/enum/constants.ts +0 -30
- package/src/enum/core.ts +0 -635
- package/src/enum/defines.ts +0 -45
- package/src/enum/extensions.ts +0 -31
- package/src/enum/mod.ts +0 -6
- package/src/enum/prelude.ts +0 -619
- package/src/enum/symbols.ts +0 -9
- package/src/enum/utils.ts +0 -27
- package/src/mod.ts +0 -1
package/README.cn.md
CHANGED
|
@@ -1,47 +1,293 @@
|
|
|
1
|
-
#
|
|
1
|
+
# happy-rusty
|
|
2
|
+
|
|
3
|
+
将 Rust 的 `Option`、`Result` 和同步原语带入 JavaScript/TypeScript - 更好的错误处理和空值安全模式。
|
|
2
4
|
|
|
3
5
|
[](https://npmjs.org/package/happy-rusty)
|
|
4
6
|
[](https://npmjs.org/package/happy-rusty)
|
|
5
7
|
[](https://jsr.io/@happy-js/happy-rusty)
|
|
6
8
|
[](https://jsr.io/@happy-js/happy-rusty/score)
|
|
7
|
-
[](https://github.com/JiangJie/happy-rusty/actions/workflows/test.yml)
|
|
8
10
|
[](https://codecov.io/gh/JiangJie/happy-rusty)
|
|
9
11
|
|
|
10
12
|
---
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
[English](README.md)
|
|
15
|
+
|
|
16
|
+
---
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
## 特性
|
|
19
|
+
|
|
20
|
+
- **Option<T>** - 表示可选值:每个 `Option` 要么是 `Some(T)`,要么是 `None`
|
|
21
|
+
- **Result<T, E>** - 表示成功(`Ok(T)`)或失败(`Err(E)`)
|
|
22
|
+
- **同步原语** - Rust 风格的 `Once<T>`、`Lazy<T>`、`LazyAsync<T>` 和 `Mutex<T>`
|
|
23
|
+
- **控制流** - 用于短路操作的 `ControlFlow<B, C>`,包含 `Break` 和 `Continue`
|
|
24
|
+
- **完整的 TypeScript 支持**,具有严格的类型推断
|
|
25
|
+
- **异步支持** - 所有转换方法都有异步版本
|
|
26
|
+
- **零依赖**
|
|
27
|
+
- **运行时不可变** - 所有实例都通过 `Object.freeze()` 冻结
|
|
28
|
+
- **跨运行时** - 支持 Node.js、Deno、Bun 和浏览器
|
|
16
29
|
|
|
17
30
|
## 安装
|
|
18
31
|
|
|
19
32
|
```sh
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
-
|
|
33
|
+
# npm
|
|
34
|
+
npm install happy-rusty
|
|
35
|
+
|
|
36
|
+
# yarn
|
|
23
37
|
yarn add happy-rusty
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
#
|
|
38
|
+
|
|
39
|
+
# pnpm
|
|
40
|
+
pnpm add happy-rusty
|
|
41
|
+
|
|
42
|
+
# JSR (Deno)
|
|
29
43
|
deno add @happy-js/happy-rusty
|
|
30
|
-
|
|
44
|
+
|
|
45
|
+
# JSR (Bun)
|
|
31
46
|
bunx jsr add @happy-js/happy-rusty
|
|
32
47
|
```
|
|
33
48
|
|
|
34
|
-
|
|
49
|
+
## 快速开始
|
|
35
50
|
|
|
36
51
|
```ts
|
|
37
52
|
import { Some, None, Ok, Err } from 'happy-rusty';
|
|
53
|
+
|
|
54
|
+
// Option - 处理可空值
|
|
55
|
+
function findUser(id: number): Option<User> {
|
|
56
|
+
const user = database.get(id);
|
|
57
|
+
return user ? Some(user) : None;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const user = findUser(1)
|
|
61
|
+
.map(u => u.name)
|
|
62
|
+
.unwrapOr('Guest');
|
|
63
|
+
|
|
64
|
+
// Result - 处理错误
|
|
65
|
+
function parseJSON<T>(json: string): Result<T, Error> {
|
|
66
|
+
try {
|
|
67
|
+
return Ok(JSON.parse(json));
|
|
68
|
+
} catch (e) {
|
|
69
|
+
return Err(e as Error);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const config = parseJSON<Config>(jsonStr)
|
|
74
|
+
.map(c => c.settings)
|
|
75
|
+
.unwrapOrElse(err => {
|
|
76
|
+
console.error('解析失败:', err);
|
|
77
|
+
return defaultSettings;
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## API 概览
|
|
82
|
+
|
|
83
|
+
### Option<T>
|
|
84
|
+
|
|
85
|
+
| 分类 | 方法 |
|
|
86
|
+
|------|------|
|
|
87
|
+
| **构造器** | `Some(value)`, `None` |
|
|
88
|
+
| **查询** | `isSome()`, `isNone()`, `isSomeAnd(fn)` |
|
|
89
|
+
| **提取** | `expect(msg)`, `unwrap()`, `unwrapOr(default)`, `unwrapOrElse(fn)` |
|
|
90
|
+
| **转换** | `map(fn)`, `mapOr(default, fn)`, `mapOrElse(defaultFn, fn)`, `filter(fn)`, `flatten()` |
|
|
91
|
+
| **布尔操作** | `and(other)`, `andThen(fn)`, `or(other)`, `orElse(fn)`, `xor(other)` |
|
|
92
|
+
| **类型转换** | `okOr(err)`, `okOrElse(fn)`, `transpose()` |
|
|
93
|
+
| **组合** | `zip(other)`, `zipWith(other, fn)`, `unzip()` |
|
|
94
|
+
| **副作用** | `inspect(fn)` |
|
|
95
|
+
| **比较** | `eq(other)` |
|
|
96
|
+
|
|
97
|
+
### Result<T, E>
|
|
98
|
+
|
|
99
|
+
| 分类 | 方法 |
|
|
100
|
+
|------|------|
|
|
101
|
+
| **构造器** | `Ok(value)`, `Ok()` (void), `Err(error)` |
|
|
102
|
+
| **查询** | `isOk()`, `isErr()`, `isOkAnd(fn)`, `isErrAnd(fn)` |
|
|
103
|
+
| **提取 Ok** | `expect(msg)`, `unwrap()`, `unwrapOr(default)`, `unwrapOrElse(fn)` |
|
|
104
|
+
| **提取 Err** | `expectErr(msg)`, `unwrapErr()` |
|
|
105
|
+
| **转换** | `map(fn)`, `mapErr(fn)`, `mapOr(default, fn)`, `mapOrElse(defaultFn, fn)`, `flatten()` |
|
|
106
|
+
| **布尔操作** | `and(other)`, `andThen(fn)`, `or(other)`, `orElse(fn)` |
|
|
107
|
+
| **类型转换** | `ok()`, `err()`, `transpose()` |
|
|
108
|
+
| **类型断言** | `asOk<F>()`, `asErr<U>()` |
|
|
109
|
+
| **副作用** | `inspect(fn)`, `inspectErr(fn)` |
|
|
110
|
+
| **比较** | `eq(other)` |
|
|
111
|
+
|
|
112
|
+
### 异步方法
|
|
113
|
+
|
|
114
|
+
所有转换方法都有带 `Async` 后缀的异步变体:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
// 异步 Option 方法
|
|
118
|
+
isSomeAndAsync(asyncFn)
|
|
119
|
+
unwrapOrElseAsync(asyncFn)
|
|
120
|
+
andThenAsync(asyncFn)
|
|
121
|
+
orElseAsync(asyncFn)
|
|
122
|
+
|
|
123
|
+
// 异步 Result 方法
|
|
124
|
+
isOkAndAsync(asyncFn)
|
|
125
|
+
isErrAndAsync(asyncFn)
|
|
126
|
+
unwrapOrElseAsync(asyncFn)
|
|
127
|
+
andThenAsync(asyncFn)
|
|
128
|
+
orElseAsync(asyncFn)
|
|
38
129
|
```
|
|
39
130
|
|
|
40
|
-
|
|
131
|
+
### 类型别名
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
// 常用模式的便捷类型别名
|
|
135
|
+
type AsyncOption<T> = Promise<Option<T>>;
|
|
136
|
+
type AsyncResult<T, E> = Promise<Result<T, E>>;
|
|
41
137
|
|
|
42
|
-
|
|
138
|
+
// 用于 I/O 操作
|
|
139
|
+
type IOResult<T> = Result<T, Error>;
|
|
140
|
+
type AsyncIOResult<T> = Promise<IOResult<T>>;
|
|
141
|
+
|
|
142
|
+
// 用于 void 返回值
|
|
143
|
+
type VoidResult<E> = Result<void, E>;
|
|
144
|
+
type VoidIOResult = IOResult<void>;
|
|
145
|
+
type AsyncVoidResult<E> = Promise<VoidResult<E>>;
|
|
146
|
+
type AsyncVoidIOResult = Promise<VoidIOResult>;
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 工具函数
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import { isOption, isResult, isControlFlow, promiseToAsyncResult } from 'happy-rusty';
|
|
153
|
+
|
|
154
|
+
// 类型守卫
|
|
155
|
+
if (isOption(value)) { /* ... */ }
|
|
156
|
+
if (isResult(value)) { /* ... */ }
|
|
157
|
+
if (isControlFlow(value)) { /* ... */ }
|
|
158
|
+
|
|
159
|
+
// 将 Promise 转换为 Result
|
|
160
|
+
const result = await promiseToAsyncResult(fetch('/api/data'));
|
|
161
|
+
result.inspect(data => console.log(data))
|
|
162
|
+
.inspectErr(err => console.error(err));
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 常量
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { RESULT_TRUE, RESULT_FALSE, RESULT_ZERO, RESULT_VOID } from 'happy-rusty';
|
|
169
|
+
|
|
170
|
+
// 可复用的不可变 Result 常量
|
|
171
|
+
function validate(): Result<boolean, Error> {
|
|
172
|
+
return isValid ? RESULT_TRUE : RESULT_FALSE;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function doSomething(): Result<void, Error> {
|
|
176
|
+
// ...
|
|
177
|
+
return RESULT_VOID;
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 同步原语
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
import { Once, Lazy, LazyAsync, Mutex } from 'happy-rusty';
|
|
185
|
+
|
|
186
|
+
// Once - 一次性初始化(类似 Rust 的 OnceLock)
|
|
187
|
+
const config = Once<Config>();
|
|
188
|
+
config.set(loadConfig()); // 只能设置一次
|
|
189
|
+
config.get(); // Some(config) 或 None
|
|
190
|
+
config.getOrInit(() => defaultCfg); // 获取或初始化
|
|
191
|
+
|
|
192
|
+
// Lazy - 惰性初始化,在构造时提供初始化函数
|
|
193
|
+
const expensive = Lazy(() => computeExpensiveValue());
|
|
194
|
+
expensive.force(); // 首次访问时计算,之后缓存
|
|
195
|
+
|
|
196
|
+
// LazyAsync - 异步惰性初始化
|
|
197
|
+
const db = LazyAsync(async () => await Database.connect(url));
|
|
198
|
+
await db.force(); // 只建立一次连接,并发调用会等待
|
|
199
|
+
|
|
200
|
+
// Mutex - 异步互斥锁
|
|
201
|
+
const state = Mutex({ count: 0 });
|
|
202
|
+
await state.withLock(async (s) => {
|
|
203
|
+
s.count += 1; // 独占访问
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 控制流
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
import { Break, Continue, ControlFlow } from 'happy-rusty';
|
|
211
|
+
|
|
212
|
+
// 短路操作
|
|
213
|
+
function findFirst<T>(arr: T[], pred: (t: T) => boolean): Option<T> {
|
|
214
|
+
for (const item of arr) {
|
|
215
|
+
const flow = pred(item) ? Break(item) : Continue();
|
|
216
|
+
if (flow.isBreak()) {
|
|
217
|
+
return Some(flow.breakValue().unwrap());
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return None;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// 带提前退出的自定义 fold
|
|
224
|
+
function tryFold<T, Acc>(
|
|
225
|
+
arr: T[],
|
|
226
|
+
init: Acc,
|
|
227
|
+
f: (acc: Acc, item: T) => ControlFlow<Acc, Acc>
|
|
228
|
+
): Acc {
|
|
229
|
+
let acc = init;
|
|
230
|
+
for (const item of arr) {
|
|
231
|
+
const flow = f(acc, item);
|
|
232
|
+
if (flow.isBreak()) return flow.breakValue().unwrap();
|
|
233
|
+
acc = flow.continueValue().unwrap();
|
|
234
|
+
}
|
|
235
|
+
return acc;
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## 示例
|
|
240
|
+
|
|
241
|
+
- [Option 基础用法](examples/option.ts)
|
|
43
242
|
- [AsyncOption](examples/option.async.ts)
|
|
44
|
-
- [Result](examples/result.ts)
|
|
243
|
+
- [Result 基础用法](examples/result.ts)
|
|
45
244
|
- [AsyncResult](examples/result.async.ts)
|
|
245
|
+
- [Once](examples/once.ts)
|
|
246
|
+
- [Lazy](examples/lazy.ts)
|
|
247
|
+
- [Mutex](examples/mutex.ts)
|
|
248
|
+
- [ControlFlow](examples/control_flow.ts)
|
|
249
|
+
|
|
250
|
+
## 文档
|
|
251
|
+
|
|
252
|
+
完整的 API 文档请查看 [jiangjie.github.io/happy-rusty](https://jiangjie.github.io/happy-rusty/)。
|
|
253
|
+
|
|
254
|
+
## 设计说明
|
|
255
|
+
|
|
256
|
+
### 不可变性
|
|
257
|
+
|
|
258
|
+
所有类型(`Option`、`Result`、`ControlFlow`、`Lazy`、`LazyAsync`、`Once`、`Mutex`、`MutexGuard`)都通过 `Object.freeze()` 实现**运行时不可变**。这可以防止意外修改方法或属性:
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
const some = Some(42);
|
|
262
|
+
some.unwrap = () => 0; // TypeError: Cannot assign to read only property
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**为什么 TypeScript 接口没有使用 `readonly`?**
|
|
266
|
+
|
|
267
|
+
我们有意在接口的方法签名中省略了 `readonly` 修饰符。虽然这看起来可能会降低类型安全性,但有以下几个原因:
|
|
268
|
+
|
|
269
|
+
1. **继承兼容性** - `None` 类型继承自 `Option<never>`。TypeScript 的箭头函数属性语法(`readonly prop: () => T`)使用逆变参数检查,这会导致 `None`(参数为 `never`)与 `Option<T>` 不兼容。方法语法(`method(): T`)使用双变参数检查,使继承能够正常工作。
|
|
270
|
+
|
|
271
|
+
2. **运行时保护已足够** - `Object.freeze()` 已经在运行时阻止了重新赋值。添加 `readonly` 只提供编译时检查,在已有运行时保护的情况下收益有限。
|
|
272
|
+
|
|
273
|
+
3. **更简洁的 API** - 避免使用 `Mutable*` + `Readonly<>` 模式可以保持导出类型的简洁和文档的可读性。
|
|
274
|
+
|
|
275
|
+
4. **测试验证不可变性** - 我们的测试套件明确验证了所有实例都被冻结并且会拒绝属性修改。
|
|
276
|
+
|
|
277
|
+
## 为什么选择 happy-rusty?
|
|
278
|
+
|
|
279
|
+
JavaScript 的 `null`/`undefined` 和 try-catch 模式会导致:
|
|
280
|
+
- 未捕获的空引用错误
|
|
281
|
+
- 遗忘的错误处理
|
|
282
|
+
- 冗长的 try-catch 代码块
|
|
283
|
+
- 不清晰的函数契约
|
|
284
|
+
|
|
285
|
+
`happy-rusty` 提供了 Rust 久经考验的模式:
|
|
286
|
+
- **显式可选性** - `Option<T>` 在类型中明确表示值的缺失
|
|
287
|
+
- **显式错误** - `Result<T, E>` 强制考虑错误处理
|
|
288
|
+
- **链式调用** - 无需嵌套 if-else 或 try-catch 即可转换值
|
|
289
|
+
- **类型安全** - 完整的 TypeScript 支持,具有严格的类型推断
|
|
290
|
+
|
|
291
|
+
## 许可证
|
|
46
292
|
|
|
47
|
-
|
|
293
|
+
[MIT](LICENSE)
|
package/README.md
CHANGED
|
@@ -1,53 +1,293 @@
|
|
|
1
|
-
#
|
|
1
|
+
# happy-rusty
|
|
2
|
+
|
|
3
|
+
Rust's `Option`, `Result`, and sync primitives for JavaScript/TypeScript - Better error handling and null-safety patterns.
|
|
2
4
|
|
|
3
5
|
[](https://npmjs.org/package/happy-rusty)
|
|
4
6
|
[](https://npmjs.org/package/happy-rusty)
|
|
5
7
|
[](https://jsr.io/@happy-js/happy-rusty)
|
|
6
8
|
[](https://jsr.io/@happy-js/happy-rusty/score)
|
|
7
|
-
[](https://github.com/JiangJie/happy-rusty/actions/workflows/test.yml)
|
|
8
10
|
[](https://codecov.io/gh/JiangJie/happy-rusty)
|
|
9
11
|
|
|
10
12
|
---
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
[中文](README.cn.md)
|
|
13
15
|
|
|
14
16
|
---
|
|
15
17
|
|
|
16
|
-
##
|
|
18
|
+
## Features
|
|
17
19
|
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
+
- **Option<T>** - Represents an optional value: every `Option` is either `Some(T)` or `None`
|
|
21
|
+
- **Result<T, E>** - Represents either success (`Ok(T)`) or failure (`Err(E)`)
|
|
22
|
+
- **Sync Primitives** - Rust-inspired `Once<T>`, `Lazy<T>`, `LazyAsync<T>`, and `Mutex<T>`
|
|
23
|
+
- **Control Flow** - `ControlFlow<B, C>` with `Break` and `Continue` for short-circuiting operations
|
|
24
|
+
- **Full TypeScript support** with strict type inference
|
|
25
|
+
- **Async support** - Async versions of all transformation methods
|
|
26
|
+
- **Zero dependencies**
|
|
27
|
+
- **Runtime immutability** - All instances are frozen with `Object.freeze()`
|
|
28
|
+
- **Cross-runtime** - Works in Node.js, Deno, Bun, and browsers
|
|
20
29
|
|
|
21
30
|
## Installation
|
|
22
31
|
|
|
23
32
|
```sh
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
# npm
|
|
34
|
+
npm install happy-rusty
|
|
35
|
+
|
|
36
|
+
# yarn
|
|
27
37
|
yarn add happy-rusty
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
#
|
|
38
|
+
|
|
39
|
+
# pnpm
|
|
40
|
+
pnpm add happy-rusty
|
|
41
|
+
|
|
42
|
+
# JSR (Deno)
|
|
33
43
|
deno add @happy-js/happy-rusty
|
|
34
|
-
|
|
44
|
+
|
|
45
|
+
# JSR (Bun)
|
|
35
46
|
bunx jsr add @happy-js/happy-rusty
|
|
36
47
|
```
|
|
37
48
|
|
|
38
|
-
|
|
49
|
+
## Quick Start
|
|
39
50
|
|
|
40
51
|
```ts
|
|
41
52
|
import { Some, None, Ok, Err } from 'happy-rusty';
|
|
53
|
+
|
|
54
|
+
// Option - handling nullable values
|
|
55
|
+
function findUser(id: number): Option<User> {
|
|
56
|
+
const user = database.get(id);
|
|
57
|
+
return user ? Some(user) : None;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const user = findUser(1)
|
|
61
|
+
.map(u => u.name)
|
|
62
|
+
.unwrapOr('Guest');
|
|
63
|
+
|
|
64
|
+
// Result - handling errors
|
|
65
|
+
function parseJSON<T>(json: string): Result<T, Error> {
|
|
66
|
+
try {
|
|
67
|
+
return Ok(JSON.parse(json));
|
|
68
|
+
} catch (e) {
|
|
69
|
+
return Err(e as Error);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const config = parseJSON<Config>(jsonStr)
|
|
74
|
+
.map(c => c.settings)
|
|
75
|
+
.unwrapOrElse(err => {
|
|
76
|
+
console.error('Parse failed:', err);
|
|
77
|
+
return defaultSettings;
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## API Overview
|
|
82
|
+
|
|
83
|
+
### Option<T>
|
|
84
|
+
|
|
85
|
+
| Category | Methods |
|
|
86
|
+
|----------|---------|
|
|
87
|
+
| **Constructors** | `Some(value)`, `None` |
|
|
88
|
+
| **Querying** | `isSome()`, `isNone()`, `isSomeAnd(fn)` |
|
|
89
|
+
| **Extracting** | `expect(msg)`, `unwrap()`, `unwrapOr(default)`, `unwrapOrElse(fn)` |
|
|
90
|
+
| **Transforming** | `map(fn)`, `mapOr(default, fn)`, `mapOrElse(defaultFn, fn)`, `filter(fn)`, `flatten()` |
|
|
91
|
+
| **Boolean ops** | `and(other)`, `andThen(fn)`, `or(other)`, `orElse(fn)`, `xor(other)` |
|
|
92
|
+
| **Converting** | `okOr(err)`, `okOrElse(fn)`, `transpose()` |
|
|
93
|
+
| **Combining** | `zip(other)`, `zipWith(other, fn)`, `unzip()` |
|
|
94
|
+
| **Side effects** | `inspect(fn)` |
|
|
95
|
+
| **Comparison** | `eq(other)` |
|
|
96
|
+
|
|
97
|
+
### Result<T, E>
|
|
98
|
+
|
|
99
|
+
| Category | Methods |
|
|
100
|
+
|----------|---------|
|
|
101
|
+
| **Constructors** | `Ok(value)`, `Ok()` (void), `Err(error)` |
|
|
102
|
+
| **Querying** | `isOk()`, `isErr()`, `isOkAnd(fn)`, `isErrAnd(fn)` |
|
|
103
|
+
| **Extracting Ok** | `expect(msg)`, `unwrap()`, `unwrapOr(default)`, `unwrapOrElse(fn)` |
|
|
104
|
+
| **Extracting Err** | `expectErr(msg)`, `unwrapErr()` |
|
|
105
|
+
| **Transforming** | `map(fn)`, `mapErr(fn)`, `mapOr(default, fn)`, `mapOrElse(defaultFn, fn)`, `flatten()` |
|
|
106
|
+
| **Boolean ops** | `and(other)`, `andThen(fn)`, `or(other)`, `orElse(fn)` |
|
|
107
|
+
| **Converting** | `ok()`, `err()`, `transpose()` |
|
|
108
|
+
| **Type casting** | `asOk<F>()`, `asErr<U>()` |
|
|
109
|
+
| **Side effects** | `inspect(fn)`, `inspectErr(fn)` |
|
|
110
|
+
| **Comparison** | `eq(other)` |
|
|
111
|
+
|
|
112
|
+
### Async Methods
|
|
113
|
+
|
|
114
|
+
All transformation methods have async variants with `Async` suffix:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
// Async Option methods
|
|
118
|
+
isSomeAndAsync(asyncFn)
|
|
119
|
+
unwrapOrElseAsync(asyncFn)
|
|
120
|
+
andThenAsync(asyncFn)
|
|
121
|
+
orElseAsync(asyncFn)
|
|
122
|
+
|
|
123
|
+
// Async Result methods
|
|
124
|
+
isOkAndAsync(asyncFn)
|
|
125
|
+
isErrAndAsync(asyncFn)
|
|
126
|
+
unwrapOrElseAsync(asyncFn)
|
|
127
|
+
andThenAsync(asyncFn)
|
|
128
|
+
orElseAsync(asyncFn)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Type Aliases
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
// Convenient type aliases for common patterns
|
|
135
|
+
type AsyncOption<T> = Promise<Option<T>>;
|
|
136
|
+
type AsyncResult<T, E> = Promise<Result<T, E>>;
|
|
137
|
+
|
|
138
|
+
// For I/O operations
|
|
139
|
+
type IOResult<T> = Result<T, Error>;
|
|
140
|
+
type AsyncIOResult<T> = Promise<IOResult<T>>;
|
|
141
|
+
|
|
142
|
+
// For void returns
|
|
143
|
+
type VoidResult<E> = Result<void, E>;
|
|
144
|
+
type VoidIOResult = IOResult<void>;
|
|
145
|
+
type AsyncVoidResult<E> = Promise<VoidResult<E>>;
|
|
146
|
+
type AsyncVoidIOResult = Promise<VoidIOResult>;
|
|
42
147
|
```
|
|
43
148
|
|
|
44
|
-
|
|
149
|
+
### Utility Functions
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import { isOption, isResult, isControlFlow, promiseToAsyncResult } from 'happy-rusty';
|
|
153
|
+
|
|
154
|
+
// Type guards
|
|
155
|
+
if (isOption(value)) { /* ... */ }
|
|
156
|
+
if (isResult(value)) { /* ... */ }
|
|
157
|
+
if (isControlFlow(value)) { /* ... */ }
|
|
158
|
+
|
|
159
|
+
// Convert Promise to Result
|
|
160
|
+
const result = await promiseToAsyncResult(fetch('/api/data'));
|
|
161
|
+
result.inspect(data => console.log(data))
|
|
162
|
+
.inspectErr(err => console.error(err));
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Constants
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { RESULT_TRUE, RESULT_FALSE, RESULT_ZERO, RESULT_VOID } from 'happy-rusty';
|
|
45
169
|
|
|
46
|
-
|
|
170
|
+
// Reusable immutable Result constants
|
|
171
|
+
function validate(): Result<boolean, Error> {
|
|
172
|
+
return isValid ? RESULT_TRUE : RESULT_FALSE;
|
|
173
|
+
}
|
|
47
174
|
|
|
48
|
-
|
|
175
|
+
function doSomething(): Result<void, Error> {
|
|
176
|
+
// ...
|
|
177
|
+
return RESULT_VOID;
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Sync Primitives
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
import { Once, Lazy, LazyAsync, Mutex } from 'happy-rusty';
|
|
185
|
+
|
|
186
|
+
// Once - one-time initialization (like Rust's OnceLock)
|
|
187
|
+
const config = Once<Config>();
|
|
188
|
+
config.set(loadConfig()); // Set once
|
|
189
|
+
config.get(); // Some(config) or None
|
|
190
|
+
config.getOrInit(() => defaultCfg); // Get or initialize
|
|
191
|
+
|
|
192
|
+
// Lazy - lazy initialization with initializer at construction
|
|
193
|
+
const expensive = Lazy(() => computeExpensiveValue());
|
|
194
|
+
expensive.force(); // Compute on first access, cached thereafter
|
|
195
|
+
|
|
196
|
+
// LazyAsync - async lazy initialization
|
|
197
|
+
const db = LazyAsync(async () => await Database.connect(url));
|
|
198
|
+
await db.force(); // Only one connection, concurrent calls wait
|
|
199
|
+
|
|
200
|
+
// Mutex - async mutual exclusion
|
|
201
|
+
const state = Mutex({ count: 0 });
|
|
202
|
+
await state.withLock(async (s) => {
|
|
203
|
+
s.count += 1; // Exclusive access
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Control Flow
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
import { Break, Continue, ControlFlow } from 'happy-rusty';
|
|
211
|
+
|
|
212
|
+
// Short-circuit operations
|
|
213
|
+
function findFirst<T>(arr: T[], pred: (t: T) => boolean): Option<T> {
|
|
214
|
+
for (const item of arr) {
|
|
215
|
+
const flow = pred(item) ? Break(item) : Continue();
|
|
216
|
+
if (flow.isBreak()) {
|
|
217
|
+
return Some(flow.breakValue().unwrap());
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return None;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Custom fold with early exit
|
|
224
|
+
function tryFold<T, Acc>(
|
|
225
|
+
arr: T[],
|
|
226
|
+
init: Acc,
|
|
227
|
+
f: (acc: Acc, item: T) => ControlFlow<Acc, Acc>
|
|
228
|
+
): Acc {
|
|
229
|
+
let acc = init;
|
|
230
|
+
for (const item of arr) {
|
|
231
|
+
const flow = f(acc, item);
|
|
232
|
+
if (flow.isBreak()) return flow.breakValue().unwrap();
|
|
233
|
+
acc = flow.continueValue().unwrap();
|
|
234
|
+
}
|
|
235
|
+
return acc;
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Examples
|
|
240
|
+
|
|
241
|
+
- [Option basics](examples/option.ts)
|
|
49
242
|
- [AsyncOption](examples/option.async.ts)
|
|
50
|
-
- [Result](examples/result.ts)
|
|
243
|
+
- [Result basics](examples/result.ts)
|
|
51
244
|
- [AsyncResult](examples/result.async.ts)
|
|
245
|
+
- [Once](examples/once.ts)
|
|
246
|
+
- [Lazy](examples/lazy.ts)
|
|
247
|
+
- [Mutex](examples/mutex.ts)
|
|
248
|
+
- [ControlFlow](examples/control_flow.ts)
|
|
249
|
+
|
|
250
|
+
## Documentation
|
|
251
|
+
|
|
252
|
+
Full API documentation is available at [jiangjie.github.io/happy-rusty](https://jiangjie.github.io/happy-rusty/).
|
|
253
|
+
|
|
254
|
+
## Design Notes
|
|
255
|
+
|
|
256
|
+
### Immutability
|
|
257
|
+
|
|
258
|
+
All types (`Option`, `Result`, `ControlFlow`, `Lazy`, `LazyAsync`, `Once`, `Mutex`, `MutexGuard`) are **immutable at runtime** via `Object.freeze()`. This prevents accidental modification of methods or properties:
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
const some = Some(42);
|
|
262
|
+
some.unwrap = () => 0; // TypeError: Cannot assign to read only property
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Why no `readonly` in TypeScript interfaces?**
|
|
266
|
+
|
|
267
|
+
We intentionally omit `readonly` modifiers from method signatures in interfaces. While this might seem to reduce type safety, there are compelling reasons:
|
|
268
|
+
|
|
269
|
+
1. **Inheritance compatibility** - The `None` type extends `Option<never>`. TypeScript's arrow function property syntax (`readonly prop: () => T`) uses contravariant parameter checking, which causes `None` (with `never` parameters) to be incompatible with `Option<T>`. Method syntax (`method(): T`) uses bivariant checking, allowing the inheritance to work correctly.
|
|
270
|
+
|
|
271
|
+
2. **Runtime protection is sufficient** - `Object.freeze()` already prevents reassignment at runtime. Adding `readonly` only provides compile-time checking, which offers marginal benefit when runtime protection exists.
|
|
272
|
+
|
|
273
|
+
3. **Cleaner API** - Avoiding the `Mutable*` + `Readonly<>` pattern keeps the exported types clean and documentation readable.
|
|
274
|
+
|
|
275
|
+
4. **Testing validates immutability** - Our test suite explicitly verifies that all instances are frozen and reject property modifications.
|
|
276
|
+
|
|
277
|
+
## Why happy-rusty?
|
|
278
|
+
|
|
279
|
+
JavaScript's `null`/`undefined` and try-catch patterns lead to:
|
|
280
|
+
- Uncaught null reference errors
|
|
281
|
+
- Forgotten error handling
|
|
282
|
+
- Verbose try-catch blocks
|
|
283
|
+
- Unclear function contracts
|
|
284
|
+
|
|
285
|
+
`happy-rusty` provides Rust's battle-tested patterns:
|
|
286
|
+
- **Explicit optionality** - `Option<T>` makes absence visible in types
|
|
287
|
+
- **Explicit errors** - `Result<T, E>` forces error handling consideration
|
|
288
|
+
- **Method chaining** - Transform values without nested if-else or try-catch
|
|
289
|
+
- **Type safety** - Full TypeScript support with strict type inference
|
|
290
|
+
|
|
291
|
+
## License
|
|
52
292
|
|
|
53
|
-
|
|
293
|
+
[MIT](LICENSE)
|