nalloc

中文文档
0.5.61发布于 2026/11/04MITTypeScriptESM + CJS零依赖构建来源已验证

为TypeScript提供类似rust的选项和结果,几乎为零的分配,以获得极致的性能

中文使用文档
文档目录28 节
  1. 为什么nalloc ?
  2. 基准
  3. 安装
  4. 快速启动
  5. 它是如何工作的
  6. 来自其他图书馆吗?
  7. 从neverthrow
  8. 从fp-ts
  9. 从oxide.ts
  10. 从ts-results
  11. 从生锈
  12. 选项
  13. 结果
  14. Iter
  15. 非空的
  16. API参考
  17. 选项
  18. 结果
  19. 模式
  20. HTTP
  21. Iter
  22. 公用事业公司
  23. ESLint插件
  24. Codemod:从neverthrow迁移过来
  25. 比较
  26. 选择
  27. 许可证
  28. 贡献

nalloc

JavaScript速度下的rust般的安全。

类型安全的 Option 和 Result 为TypeScript提供近乎零的分配,以获得极致的性能。

npm version npm downloads

为什么nalloc ?

大多数选项/结果库将每个值包装在一个对象中。nalloc没有。

Other libraries:    Some(42) --> { _tag: 'Some', value: 42 }   // allocates
nalloc:            Some(42) --> 42                            // zero allocation

Other libraries:    Ok(data) --> { _tag: 'Ok', value: data }   // allocates
nalloc:            Ok(data) --> data                          // zero allocation
  • Rust-like类型 - Option<T> 和 Result<T, E> 具有完整的模式匹配和组合子
  • 接近于零的分配 - Some 和 Ok 是价值观本身。只有 Err 分配。
  • 极端的表现 -比快乐路径上的替代方案快2倍

基准

操作nallocneverthrowts-resultsoxide.ts
ok()91行动/ s75行动/ s56 m行动/ s40米运维/ s
err()55米运维/ s52米运维/ s0.1行动/ s47米运维/ s
some()85行动/ s-40米运维/ s50米运维/ s
Result.map74行动/ s53米运维/ s47米运维/ s64行动/ s
Option.map71行动/ s-52米运维/ s41米运维/ s

运行 pnpm bench 复制。

安装

npm install nalloc

快速启动

import { Option, Result, ok, err, none, pipe, gen } from 'nalloc';

// Option: the value IS the Option
const port = Option.fromNullable(process.env.PORT);
const portNum = Option.unwrapOr(port, '3000');

// Result: Ok is the value itself, Err wraps the error
const config = Result.tryCatch(
  () => JSON.parse(raw),
  () => 'invalid json',
);
const data = Result.match(
  config,
  (cfg) => cfg,
  (error) => ({ fallback: true }),
);

// gen: Rust-like ? operator with type-safe errors
const result = gen(function* ($) {
  const a = yield* $(parseNumber('10'));
  const b = yield* $(parseNumber('5'));
  return a + b;
}); // Result<number, ParseError>

// pipe: left-to-right composition
const userId = pipe(
  Result.tryCatch(() => JSON.parse(raw)),
  (r) => Result.map(r, (data) => data.userId),
  (r) => Result.unwrapOr(r, 0),
);

它是如何工作的

类型表示分配
Option<T>T | null | undefined零
Some<T>价值本身零
Nonenull 或 undefined零
Ok<T>价值本身(品牌)零
Err<E>最小的包装 { error: E }一个对象

这意味着快乐路径上的GC压力为零——您的成功值保持为普通值。

来自其他图书馆吗?

从neverthrow

// neverthrow
import { ok, err, Result } from 'neverthrow';
const result: Result<number, string> = ok(42);
result.map((x) => x * 2);

// nalloc - same concepts, faster execution
import { Result, ok, err } from 'nalloc';
const result = ok(42); // zero allocation
Result.map(result, (x) => x * 2); // function-based API

从fp-ts

// fp-ts
import { pipe } from 'fp-ts/function';
import * as O from 'fp-ts/Option';
pipe(
  O.some(42),
  O.map((x) => x * 2),
);

// nalloc - simpler, faster
import { Option, pipe } from 'nalloc';
pipe(42, (v) => Option.map(v, (x) => x * 2)); // built-in pipe, value IS the Option

从oxide.ts

// oxide.ts
import { Some, None, Ok, Err } from 'oxide.ts';
const opt = Some(42);
opt.map((x) => x * 2);
const result = Ok(42);
result.mapErr((e) => new Error(e));

// nalloc - no wrapper objects on the happy path
import { Option, Result, ok } from 'nalloc';
Option.map(42, (x) => x * 2); // 42 is the Option itself
const result = ok(42); // zero allocation
Result.mapErr(result, (e) => new Error(e));

从ts-results

// ts-results
import { Ok, Err, Some, None } from 'ts-results';
const result = new Ok(42);
result.map((x) => x * 2);
const opt = Some(42);
opt.unwrapOr(0);

// nalloc - same safety, zero allocations
import { Option, Result, ok } from 'nalloc';
const result = ok(42); // no wrapper
Result.map(result, (x) => x * 2);
Option.unwrapOr(42, 0); // value IS the Option

从生锈

这个API反映了Rust的API Option 和 Result:

import { Option, Result, ok, err, none, gen } from 'nalloc';

// Rust: Some(42).map(|x| x * 2)
Option.map(42, (x) => x * 2);

// Rust: Ok(42).and_then(|x| if x > 0 { Ok(x) } else { Err("negative") })
Result.andThen(ok(42), (x) => (x > 0 ? ok(x) : err('negative')));

// Rust: result.unwrap_or(0)
Result.unwrapOr(result, 0);

// Rust: let value = get_value()?
const value = gen(function* ($) {
  const a = yield* $(getValue());
  return a + 1;
});

选项

一个 Option<T> 是 T | null | undefined。值本身就是Option。

import { Option, none } from 'nalloc';

// Create from nullable
const maybePort = Option.fromNullable(process.env.PORT);

// Transform
const doubled = Option.map(maybePort, (p) => parseInt(p) * 2);
const validated = Option.filter(doubled, (n) => n > 0);

// Extract
const port = Option.unwrapOr(maybePort, '3000');

// Pattern match
const label = Option.match(
  maybePort,
  (value) => `Port: ${value}`,
  () => 'Port: default',
);

// Assert and narrow
Option.assertSome(maybePort, 'PORT is required');

// Filter collections
const activeIds = Option.filterMap(users, (user) => (user.isActive ? user.id : none));

// Async
const maybeUser = await Option.fromPromise(fetchUserById(id));

结果

一个 Result<T, E> 要么是 Ok<T> (价值本身)或 Err<E> (包装)。

import { Result, Schema, ok, err, gen, genAsync, pipe } from 'nalloc';

// Wrap throwing functions
const parsed = Result.tryCatch(
  () => JSON.parse(raw),
  (e) => 'invalid json',
);

// gen: Rust-like ? operator with preserved error types
const result = gen(function* ($) {
  const config = yield* $(parseConfig(raw));
  const db = yield* $(connectDb(config.url));
  return db.query('SELECT 1');
}); // Result<QueryResult, ConfigError | DbError>

// Async gen
const data = await genAsync(async function* ($) {
  const res = yield* $(await Result.fromPromise(fetchUser(id)));
  const posts = yield* $(await Result.fromPromise(fetchPosts(res.id)));
  return { user: res, posts };
}); // Promise<Result<{user, posts}, unknown>>

// wrap / toThrowable: ecosystem boundaries
const safeParse = Result.wrap(JSON.parse); // throwing -> Result
safeParse('{"a":1}'); // Ok({a: 1})
const throwingFind = Result.toThrowable(findUser); // Result -> throwing
throwingFind('123'); // returns User or throws

// Standard Schema validation (Zod, Valibot, ArkType, etc.)
const validated = Schema.fromSchema(userSchema, input);
// Result<User, readonly SchemaIssue[]>

// wrapSchema: bind a schema once, reuse the validator
const parseUser = Schema.wrapSchema(userSchema);

// Transform with pipe
const userId = pipe(
  parsed,
  (r) => Result.map(r, (data) => data.userId),
  (r) => Result.flatMap(r, (id) => (id > 0 ? ok(id) : err('invalid id'))),
);

// Pattern match
const user = Result.match(
  parsed,
  (data) => data.user,
  (error) => null,
);

// Assert and narrow
Result.assertOk(loaded, 'Config required');

// Collections
const combined = Result.all([ok(1), ok(2), ok(3)]); // Ok([1, 2, 3])
const first = Result.any([err('a'), ok(42), err('b')]); // Ok(42)
const [oks, errs] = Result.partition(results);

Iter

用于处理易出错迭代和结果流的迭代器实用程序。

import { Iter } from 'nalloc';

// Safe iteration: wraps throws as Err, values as Ok
for (const result of Iter.safeIter(riskyIterator)) {
  if (isOk(result)) handleValue(result);
  else handleError(result.error);
}

// mapWhile: yields mapped values while fn returns Some
const taken = [...Iter.mapWhile([1, 2, 3, 4, 5], (n) => (n < 4 ? n * 10 : null))]; // [10, 20, 30]

// tryCollect: collect Results into Result<T[], E>
const collected = Iter.tryCollect(results); // Ok([1, 2, 3]) or first Err

// tryFold: fold with early exit on Err
const sum = Iter.tryFold([1, 2, 3], 0, (acc, n) => ok(acc + n));

// tryForEach: iterate with early exit on Err
Iter.tryForEach(items, (item) => processItem(item));

非空的

在类型级别证明数组至少有一个元素。运行时 Value是一个普通数组——没有包装器,没有类,没有克隆。 fromArray 返回 非空时的原始数组值。

import { NonEmpty, Option } from 'nalloc';

NonEmpty.isNonEmpty([]); // false
NonEmpty.isNonEmpty([1]); // true, narrows to ReadonlyNonEmptyArray<number>

const values: readonly number[] = loadValues();
if (NonEmpty.isNonEmpty(values)) {
  const first: number = values[0]; // typed, no undefined
}

// fromArray returns None for [] and Some(values) otherwise
const opt = NonEmpty.fromArray(values); // Option<ReadonlyNonEmptyArray<number>>

两个Result组合子现在在其错误类型中携带非空保证:

  • Result.collectAll(results) 返回 Result<T[], NonEmptyArray<E>> -如果 结果是 Err,则保证错误数组至少包含一个条目。
  • Result.any(results) 返回 Result<Widen<T>, NonEmptyArray<WidenNever<E>>> 出于同样的原因: any 只有当每个输入都失败时才会失败。

API参考

选项

函数描述
fromNullable(value)将nullable转换为Option
fromPromise(promise)对选项的承诺(拒绝=无)
map(opt, fn)变换某个值
flatMap(opt, fn)链式选项返回函数
andThen(opt, fn)flatMap别名
tap(opt, fn)副作用有的,返璞归真
tapNone(opt, fn)无副作用,返回原来的
filter(opt, predicate)如果谓词通过,则保留一些
match(opt, onSome, onNone)模式匹配
unwrap(opt)取出或丢弃
unwrapOr(opt, default)提取或默认
unwrapOrElse(opt, fn)提取或计算默认值
unwrapOrReturn(opt, fn)提取或返回计算值
expect(opt, msg)提取或抛出消息
assertSome(opt, msg?)断言并缩小到一些
isSome(opt) / isNone(opt)类型警卫
isSomeAnd(opt, pred)一些和谓词传递
isNoneOr(opt, pred)无或谓词通过
or(opt, other)先返回一些
orElse(opt, fn)返回一些或计算回退
and(opt, other)如果first是Some,则返回second
xor(opt, other)如果正好有一个是
zip(opt, other)将两个选项组合成元组
unzip(opt)将tuple Option拆分为两个
flatten(opt)平坦嵌套选项
contains(opt, value)检查Some是否包含value
mapOr(opt, default, fn)映射或返回默认值
mapOrElse(opt, defaultFn, fn)映射或计算默认值
toArray(opt)有的去[value],没有去[]
toNullable(opt)Some为value, None为null
toUndefined(opt)有些值为value,没有值为undefined
okOr(opt, error)结果选项
okOrElse(opt, fn)选项以计算错误的结果
ofOk(result)有些人对,没有人错
ofErr(result)有些人犯错,没有人好
filterMap(items, fn)映射和过滤器在一次通过
findMap(items, fn)先从映射中找到一些

结果

函数描述
tryCatch(fn, onError?)Wrap抛出函数
tryCatchMaybePromise(fn, onError?)包装同步或异步,保持同步
of(fn)tryCatch的别名(没有错误映射器)
wrap(fn, onError?)一次性封装抛出函数,重用
toThrowable(fn)wrap的反转:结果返回到抛出
gen(fn)带有输入错误的生成器do-notation
genAsync(fn)异步生成器表示法
safeTry(fn)带展开的命令式错误处理
safeTryAsync(fn)Async版本的safeTry
unwrap(result)提取Ok或抛出错误值
unwrapErr(result)提取Err或throw
unwrapOr(result, default)提取Ok或默认值
unwrapOrElse(result, fn)提取Ok或从错误中计算
unwrapOrReturn(result, fn)提取Ok或返回计算值
expect(result, msg)提取Ok或抛出消息
expectErr(result, msg)提取带有消息的Err或throw
map(result, fn)变换Ok值
mapErr(result, fn)转换Err值
bimap(result, okFn, errFn)转换Ok和Err
flatMap(result, fn)链式返回结果函数
andThen(result, fn)flatMap别名
tap(result, fn)副作用在好的,返回原来的
tapErr(result, fn)副作用Err,返回原来的
match(result, onOk, onErr)模式匹配
assertOk(result, msg?)断言并缩小为Ok
assertErr(result, msg?)断言并缩小到Err
isOk(result) / isErr(result)类型警卫
isOkAnd(result, pred)好的,谓词通过了
isErrAnd(result, pred)Err和谓词传递
isSomeErr(result)非空错误
and(result, other)如果第一个正确,则返回第二个
or(result, other)先返回Ok
orElse(result, fn)好的,或者计算错误的回退
zip(a, b)把两个Ok组合成tuple
zipWith(a, b, fn)结合两个Ok和函数
flatten(result)平坦嵌套结果
transpose(result)结果
toOption(result)有些人对,没有人错
toErrorOption(result)有些人犯错,没有人好
mapOr(result, default, fn)映射Ok或返回默认值
mapOrElse(result, defaultFn, fn)映射Ok或计算默认值
all(results)收集全部Ok或第一个Err
any(results)首先Ok or all errors
collect(results)收集Ok值或第一个Err
collectAll(results)要么一切正常,要么一切错误
partition(results)分成[ok, errors]
partitionAsync(promises)异步分区
filterOk(results)提取所有Ok值
filterErr(results)提取所有Err值
settleMaybePromise(values)将sync/async值设置为Results
partitionMaybePromise(values)分区sync/async结果

模式

标准模式v1验证。可摇树:导入 Schema 的命名空间 nalloc,或直接从 nalloc/schema。

函数描述
fromSchema(schema, value)通过标准模式v1进行验证,返回结果
wrapSchema(schema)绑定一次模式,返回一个可重用的验证器

HTTP

可选 nalloc/http 子路径。可以在任何运行时中使用 fetch (浏览器、Node 18+、Bun、Deno、edge worker);被保留在主入口之外,因此内核不受环境类型的全局变量的影响。

本地的 fetch 成功解析4xx/5xx -仅传输失败拒绝。因此,通用帮助程序单独让HTTP错误沿着Ok路径流动,看起来像成功:

const r = await Result.fromPromise(fetch(url)); // Ok(Response { status: 404 }) - not an Err!

fromFetch Ok表示请求连接并返回2xx,并且每个失败模式都落在错误通道中,输入:

import { fromFetch } from 'nalloc/http';

const res = await fromFetch(url);
// Result<Response, Response | TypeError | DOMException>
// Ok(Response)      -> connected and 2xx
// Err(Response)     -> reached the server, non-2xx (read status/body from it)
// Err(TypeError)    -> network/CORS failure
// Err(DOMException) -> aborted or timed out

已经拿着了。 Response (自定义获取包装,中间件,不同的HTTP客户端)? fromResponse 状态检查是单独的,和组合的吗 fromPromise 如果您需要分别执行这两个步骤:

import { fromResponse } from 'nalloc/http';

const checked = fromResponse(response); // Ok(response) if response.ok, else Err(response)
函数描述
fromFetch(input, init?)对于2xx运行fetch: Ok(响应),对于非2xx或传输失败运行Err
fromResponse(response)Ok(响应)如果响应。好的,else Err(response)

Iter

函数描述
safeIter(source)将iterable: values包装为Ok,抛出为Err
mapWhile(source, fn)生成映射值,而fn返回Some
tryCollect(source)收集结果到Result<T[], E>
tryFold(source, init, fn)在Err时提前退出
tryForEach(source, fn)在Err时提前退出迭代

公用事业公司

函数描述
pipe(value, ...fns)从左到右通过函数的线程值

ESLint插件

nalloc提供了一个可选的ESLint插件(flat config) nalloc/eslint。它只在开发时运行 永远不会进入运行时包。typescript-eslint工具链是一个可选的peer -安装它 除了ESLint:

npm install -D eslint @typescript-eslint/parser @typescript-eslint/utils typescript
// eslint.config.js
import nalloc from 'nalloc/eslint';
import tsParser from '@typescript-eslint/parser';

export default [
  {
    files: ['**/*.ts'],
    languageOptions: {
      parser: tsParser,
      parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname },
    },
    plugins: { nalloc },
    rules: {
      'nalloc/must-use': 'error',
      'nalloc/no-unwrap': 'error',
    },
  },
  { files: ['**/*.test.ts'], rules: { 'nalloc/no-unwrap': 'off' } },
];
规则它标记了什么类型信息
nalloc/must-use一个 Result/Option 值用作裸语句并被丢弃要求(parserOptions.projectService)
nalloc/no-unwrapunwrap / unwrapErr / expect / expectErr,这意味着失败不是必需的

nalloc.configs.recommended 打开两个规则;它不配置解析器,所以是类型敏感的 设置为 must-use 仍然是你的配置的责任。如果没有类型info, must-use 报告 标准typescript-eslint parserServices错误 no-unwrap 保持工作。

Codemod:从neverthrow迁移过来

Nalloc绑定了一个最努力的代码模块,它将neverthrow代码重写为Nalloc。它需要 可选 oxc-parser Peer(仅在开发时,不在运行时包中):

npm install -D oxc-parser
npx nalloc-codemod src --report migration-report.md   # add --dry to preview

转换内容:neverthrow导入(保留别名);方法调用到函数调用 (r.map(f) -> Result.map(r, f), .andThen -> Result.flatMap, .mapErr, .orElse, .match, .unwrapOr, .isOk/.isErr, .andTee/.orTee -> tap/tapErr, _unsafeUnwrap -> Result.unwrap); 两种或两种以上方法的链折叠成 pipe(...); fromThrowable -> Result.wrap; combine -> Result.all; combineWithAllErrors -> Result.collectAll;directly-awaited fromPromise -> Result.fromPromise; Result<T, E> 类型注释-> ResultType<T, E>。 编辑是文本拼接,因此未修改的代码保持字节对字节的格式化。

它拒绝和报告的内容: ResultAsync 以及所有产生1的东西 (okAsync, errAsync,链接 fromPromise, asyncMap,……)- nalloc习语是 genAsync, 这是手工重写; safeTry 发电机(使用 gen); 名称空间进口 (import * as nt). 它们保留了一个剩余的neverthrow导入,因此文件仍然在迁移过程中编译。

两个安全属性要知道。转换是基于来源的:方法名称有歧义,例如 .map 只有当接收者可以证明持有一个结果(构造自 ok/err/combine/ 一个 fromThrowable-包装函数,用neverthrow类型注释,或用 永远只抛出方法)-一个裸 .map 在一个未知的接收器上,假设它是一个数组并离开 独自一人。并在一次运行中迁移整个项目:转换的调用站点假设它们的值为 Nalloc值,它只在生成它们的构造函数也被转换后才有效。

比较

特征nallocneverthrowfp-tsoxide.tsts-results
Zero-alloc选项是否否否否
Zero-alloc好是否否否否
包大小小小大小小
学习曲线低低高低低
异步支持是是是有限的否
Tree-shakeable是是是是是
迭代器实用工具是否是否否
创(?操作符)是是否否否
管是否是否否
模式验证是否否否否
生态系统互操作是否否否否

选择

寻找TypeScript的结果/选项库?下面是nalloc的对比:

  • neverthrow -流行的方法链API。为每个Ok和Err分配包装器。Nalloc完全避免了快乐路径上的分配。
  • Fp-ts /效应 -全功能编程生态系统,可选可选。强大但沉重和陡峭的学习曲线。nalloc用一个更简单的API关注结果/选项。
  • oxide.ts - rust灵感与基于类的包装。每个Some/Ok分配一个对象。nalloc将Some/Ok表示为原始值。
  • Ts-results / Ts-results -results -直接Rust端口与类实例。类似于nalloc的API表面,但在每个构造上分配。
  • 真理神话 -结果和可能与功能API。包装所有值。Nalloc是类似的零分配设计理念。

Nalloc是为分配压力很重要的代码库而设计的——高吞吐量服务器、热循环和性能敏感路径——同时保持相同的安全保证。

许可证

麻省理工学院

贡献

贡献欢迎!打开issue或PR获取想法、bug修复或文档。

版本与发布记录

nalloc 共发布 49 个版本,首次发布于 2026/01/23;过去 12 个月发布了 49 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.5.61latest2026/11/04今天
0.5.602026/11/031 天前
0.5.592026/10/314 天前
0.5.582026/10/287 天前
0.5.572026/10/278 天前
0.5.562026/10/2312 天前

关于 nalloc 的常见问题

nalloc 是什么?

为TypeScript提供类似rust的选项和结果,几乎为零的分配,以获得极致的性能

如何安装 nalloc?

在项目目录运行 npm install nalloc,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 nalloc-codemod,可以用 npx nalloc-codemod 直接运行。

nalloc 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

nalloc 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

nalloc 可以商用吗?

nalloc 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

nalloc 还在维护吗?

最新版本 0.5.61 发布于 2026/11/04(今天);至今共发布 49 个版本,其中过去 12 个月发布了 49 个;首次发布于 2026/01/23。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。