https://github.com/jiangjie/happy-rusty
Rust's Option, Result, and sync primitives for JavaScript/TypeScript - Better error handling and null-safety patterns.
https://github.com/jiangjie/happy-rusty
async channel concurrency controlflow err error-handling fnonce lazy mutex none null-safety ok once option result rust rwlock some typescript
Last synced: about 2 months ago
JSON representation
Rust's Option, Result, and sync primitives for JavaScript/TypeScript - Better error handling and null-safety patterns.
- Host: GitHub
- URL: https://github.com/jiangjie/happy-rusty
- Owner: JiangJie
- License: mit
- Created: 2024-04-24T12:47:26.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2026-01-16T03:50:55.000Z (about 2 months ago)
- Last Synced: 2026-01-16T18:15:59.936Z (about 2 months ago)
- Topics: async, channel, concurrency, controlflow, err, error-handling, fnonce, lazy, mutex, none, null-safety, ok, once, option, result, rust, rwlock, some, typescript
- Language: TypeScript
- Homepage:
- Size: 930 KB
- Stars: 5
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.cn.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# happy-rusty
[](LICENSE)
[](https://github.com/JiangJie/happy-rusty/actions/workflows/test.yml)
[](https://codecov.io/gh/JiangJie/happy-rusty)
[](https://npmjs.org/package/happy-rusty)
[](https://npmjs.org/package/happy-rusty)
[](https://jsr.io/@happy-js/happy-rusty)
[](https://jsr.io/@happy-js/happy-rusty/score)
将 Rust 的 `Option`、`Result` 和同步原语带入 JavaScript/TypeScript - 更好的错误处理和空值安全模式。
---
[English](README.md) | [API 文档](https://jiangjie.github.io/happy-rusty/)
---
## 特性
- **Option<T>** - 表示可选值:每个 `Option` 要么是 `Some(T)`,要么是 `None`
- **Result<T, E>** - 表示成功(`Ok(T)`)或失败(`Err(E)`)
- **同步原语** - Rust 风格的 `Once`、`OnceAsync`、`Lazy`、`LazyAsync`、`Mutex`、`RwLock` 和 `Channel`
- **控制流** - 用于短路操作的 `ControlFlow`,包含 `Break` 和 `Continue`
- **FnOnce** - 一次性可调用函数封装(`FnOnce` 和 `FnOnceAsync`)
- **完整的 TypeScript 支持**,具有严格的类型推断
- **异步支持** - 所有转换方法都有异步版本
- **零依赖**
- **运行时不可变** - 所有实例都通过 `Object.freeze()` 冻结
- **跨运行时** - 支持 Node.js、Deno、Bun 和浏览器
## 安装
```sh
# npm
npm install happy-rusty
# yarn
yarn add happy-rusty
# pnpm
pnpm add happy-rusty
# JSR (Deno)
deno add @happy-js/happy-rusty
# JSR (Bun)
bunx jsr add @happy-js/happy-rusty
```
## 快速开始
```ts
import { Some, None, Ok, Err } from 'happy-rusty';
// Option - 处理可空值
function findUser(id: number): Option {
const user = database.get(id);
return user ? Some(user) : None;
}
const user = findUser(1)
.map(u => u.name)
.unwrapOr('Guest');
// Result - 处理错误
function parseJSON(json: string): Result {
try {
return Ok(JSON.parse(json));
} catch (e) {
return Err(e as Error);
}
}
const config = parseJSON(jsonStr)
.map(c => c.settings)
.unwrapOrElse(err => {
console.error('解析失败:', err);
return defaultSettings;
});
```
## API 概览
### Option<T>
| 分类 | 方法 |
|------|------|
| **构造器** | `Some(value)`, `None` |
| **查询** | `isSome()`, `isNone()`, `isSomeAnd(fn)` |
| **提取** | `expect(msg)`, `unwrap()`, `unwrapOr(default)`, `unwrapOrElse(fn)` |
| **转换** | `map(fn)`, `mapOr(default, fn)`, `mapOrElse(defaultFn, fn)`, `filter(fn)`, `flatten()` |
| **布尔操作** | `and(other)`, `andThen(fn)`, `or(other)`, `orElse(fn)`, `xor(other)` |
| **类型转换** | `okOr(err)`, `okOrElse(fn)`, `transpose()` |
| **组合** | `zip(other)`, `zipWith(other, fn)`, `unzip()` |
| **副作用** | `inspect(fn)` |
| **比较** | `eq(other)` |
### Result<T, E>
| 分类 | 方法 |
|------|------|
| **构造器** | `Ok(value)`, `Ok()` (void), `Err(error)` |
| **查询** | `isOk()`, `isErr()`, `isOkAnd(fn)`, `isErrAnd(fn)` |
| **提取 Ok** | `expect(msg)`, `unwrap()`, `unwrapOr(default)`, `unwrapOrElse(fn)` |
| **提取 Err** | `expectErr(msg)`, `unwrapErr()` |
| **转换** | `map(fn)`, `mapErr(fn)`, `mapOr(default, fn)`, `mapOrElse(defaultFn, fn)`, `flatten()` |
| **布尔操作** | `and(other)`, `andThen(fn)`, `or(other)`, `orElse(fn)` |
| **类型转换** | `ok()`, `err()`, `transpose()` |
| **类型断言** | `asOk()`, `asErr()` |
| **副作用** | `inspect(fn)`, `inspectErr(fn)` |
| **比较** | `eq(other)` |
### 异步方法
所有转换方法都有带 `Async` 后缀的异步变体(如 `andThenAsync`、`mapAsync`、`unwrapOrElseAsync`)。
### 类型别名
```ts
type AsyncOption = Promise>;
type AsyncResult = Promise>;
type IOResult = Result; // 用于 I/O 操作
type AsyncIOResult = Promise>; // 异步 I/O 操作
```
### 工具函数
```ts
import { tryResult, tryAsyncResult } from 'happy-rusty';
// 捕获异常为 Result(类似 Promise.try,但返回 Result)
const parsed = tryResult(JSON.parse, jsonString); // Ok(value) 或 Err(error)
const response = await tryAsyncResult(fetch, '/api/data');
```
### 同步原语
```ts
import { Lazy, LazyAsync, Mutex, Channel } from 'happy-rusty';
// Lazy - 首次访问时计算一次
const expensive = Lazy(() => computeExpensiveValue());
expensive.force(); // 计算一次后缓存
// LazyAsync - 异步惰性初始化(并发安全)
const db = LazyAsync(async () => Database.connect(url));
await db.force(); // 只建立一次连接,并发调用会等待
// Mutex - 异步互斥锁
const state = Mutex({ count: 0 });
await state.withLock(async (s) => { s.count += 1; });
// Channel - MPMC 异步消息传递
const ch = Channel(10); // 有界容量
await ch.send('hello');
for await (const msg of ch) { console.log(msg); }
```
## 示例
- [Option](examples/core/option/option.ts) / [AsyncOption](examples/core/option/option.async.ts)
- [Result](examples/core/result/result.ts) / [AsyncResult](examples/core/result/result.async.ts)
- [Once](examples/std/sync/once.ts) / [OnceAsync](examples/std/sync/once_async.ts)
- [Lazy](examples/std/sync/lazy.ts) / [LazyAsync](examples/std/sync/lazy_async.ts)
- [Mutex](examples/std/sync/mutex.ts)
- [RwLock](examples/std/sync/rwlock.ts)
- [Channel](examples/std/sync/channel.ts)
- [ControlFlow](examples/std/ops/control_flow.ts)
- [FnOnce](examples/std/ops/fn_once.ts) / [FnOnceAsync](examples/std/ops/fn_once_async.ts)
## 设计说明
### 不可变性
所有类型(`Option`、`Result`、`ControlFlow`、`Lazy`、`LazyAsync`、`Once`、`OnceAsync`、`Mutex`、`MutexGuard`、`RwLock`、`Channel`、`Sender`、`Receiver`、`FnOnce`、`FnOnceAsync`)都通过 `Object.freeze()` 实现**运行时不可变**。这可以防止意外修改方法或属性:
```ts
const some = Some(42);
some.unwrap = () => 0; // TypeError: Cannot assign to read only property
```
**为什么 TypeScript 接口没有使用 `readonly`?**
我们有意在接口的方法签名中省略了 `readonly` 修饰符。虽然这看起来可能会降低类型安全性,但有以下几个原因:
1. **继承兼容性** - `None` 类型继承自 `Option`。TypeScript 的箭头函数属性语法(`readonly prop: () => T`)使用逆变参数检查,这会导致 `None`(参数为 `never`)与 `Option` 不兼容。方法语法(`method(): T`)使用双变参数检查,使继承能够正常工作。
2. **运行时保护已足够** - `Object.freeze()` 已经在运行时阻止了重新赋值。添加 `readonly` 只提供编译时检查,在已有运行时保护的情况下收益有限。
3. **更简洁的 API** - 避免使用 `Mutable*` + `Readonly<>` 模式可以保持导出类型的简洁和文档的可读性。
4. **测试验证不可变性** - 我们的测试套件明确验证了所有实例都被冻结并且会拒绝属性修改。
## 为什么选择 happy-rusty?
JavaScript 的 `null`/`undefined` 和 try-catch 模式会导致:
- 未捕获的空引用错误
- 遗忘的错误处理
- 冗长的 try-catch 代码块
- 不清晰的函数契约
`happy-rusty` 提供了 Rust 久经考验的模式:
- **显式可选性** - `Option` 在类型中明确表示值的缺失
- **显式错误** - `Result` 强制考虑错误处理
- **链式调用** - 无需嵌套 if-else 或 try-catch 即可转换值
- **类型安全** - 完整的 TypeScript 支持,具有严格的类型推断
## 许可证
[MIT](LICENSE)