An open API service indexing awesome lists of open source software.

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.

Awesome Lists containing this project

README

          

# happy-rusty

[![License](https://img.shields.io/npm/l/happy-rusty.svg)](LICENSE)
[![Build Status](https://github.com/JiangJie/happy-rusty/actions/workflows/test.yml/badge.svg)](https://github.com/JiangJie/happy-rusty/actions/workflows/test.yml)
[![codecov](https://codecov.io/gh/JiangJie/happy-rusty/graph/badge.svg)](https://codecov.io/gh/JiangJie/happy-rusty)
[![NPM version](https://img.shields.io/npm/v/happy-rusty.svg)](https://npmjs.org/package/happy-rusty)
[![NPM downloads](https://badgen.net/npm/dm/happy-rusty)](https://npmjs.org/package/happy-rusty)
[![JSR Version](https://jsr.io/badges/@happy-js/happy-rusty)](https://jsr.io/@happy-js/happy-rusty)
[![JSR Score](https://jsr.io/badges/@happy-js/happy-rusty/score)](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)