# @emyann/tsverify 中文文档

规范地址：https://npmcn.com/package/%40emyann/tsverify/

版本：0\.1\.0
npm 元数据更新时间：2026-11-01T00:28:05.149Z
中文译文更新时间：2026-11-01T02:17:44.267Z
软件包许可：MIT

本文由上游 README 机器翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40emyann/tsverify
原文对照：https://npmcn.com/package/%40emyann/tsverify/?lang=original
项目主页：https://github.com/emyann/tsverify#readme
源码仓库：https://github.com/emyann/tsverify

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：622 kB，74 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=24\.16\.0
- 运行依赖：4 个
- peer 依赖：effect ^4\.0\.6，lodash-es ^4\.17\.21
- 首次发布：2026-11-01T00:26:28.535Z
- 最新版本发布：2026-11-01T00:28:05.004Z
- 已发布版本数：2（近 12 个月 2 个）

## 中文简介

为每个输入证明普通TypeScript的规则，或者得到一个打破规则的输入：从源读取，由Z3求解。

## 安装

```sh
npm install @emyann/tsverify
```

也可以使用：`pnpm add @emyann/tsverify`、`yarn add @emyann/tsverify`、`bun add @emyann/tsverify`

## README

![tsverify logo](https://npmcn.com/package/%40emyann/tsverify/media/logo.svg)

# tsverify

### _证明你的TypeScript代码对每个输入都保持了它的规范，或者得到破坏它的输入。_

[![CI](https://github.com/emyann/tsverify/actions/workflows/ci.yml/badge.svg)](https://github.com/emyann/tsverify/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/github/license/emyann/tsverify)](https://npmcn.com/package/%40emyann/tsverify/LICENSE) ![Node 24.16 or later](https://img.shields.io/badge/node-%E2%89%A5%2024.16-339933?logo=nodedotjs&logoColor=white) ![Solver: Z3](https://img.shields.io/badge/solver-Z3-6f42c1) [![Status: prototype](https://img.shields.io/badge/status-prototype-orange)](https://npmcn.com/package/%40emyann/tsverify/#readme-status)

你用普通的TypeScript写一个规范：函数的类型，以及它必须遵守的规则。Tsverify读取规范和代码 因为他们是，而问 [Z3](https://github.com/Z3Prover/z3) 求解器，对于每个定律，是否有输入打破了它。这个 答案是证明，或者说是输入。

![Your spec and your code go into tsverify, which asks Z3 whether any input breaks a law: if none does, the law is proved for every input; if one does, you get that input, a counterexample your real code fails on.](https://npmcn.com/package/%40emyann/tsverify/media/flow-light.svg)

```ts
// src/shipping.contract.ts: what `shipping` must do, written before the code.
import { spec } from "@emyann/tsverify"

const shippingSpec = spec<(total: number) => number>({
  // What every caller promises: a cart's total is never negative.
  requires: (total) => total >= 0,
  ensures: {
    // A law, with what it means in words: the report prints them.
    freeFrom50: {
      says: "An order of 50 or more ships free",
      holds: (total, cost) => (total >= 50 ? cost === 0 : true),
    },
    // A law alone: a smaller order pays 5.
    smallOrdersPay: (total, cost) => (total < 50 ? cost === 5 : true),
  },
})
// Its type, which the code takes.
export type ShippingSpec = typeof shippingSpec
```

```ts
// src/shipping.ts: the code takes its type from the spec, and imports nothing that runs.
import type { Implementation } from "@emyann/tsverify"
import type { ShippingSpec } from "./shipping.contract.ts"

export const shipping: Implementation<ShippingSpec> = (total) => (total >= 50 ? 0 : 5)
```

```text
$ npx tsverify
src/shipping.ts
  ✓ shipping  2 laws  (210 ms)
    ✓ An order of 50 or more ships free  (freeFrom50, 155 ms)
    ✓ smallOrdersPay  (16 ms)

✓ 2 laws proved
  1 contract in 1 file · 1.2 s
```

已证明的方法 _每一个_ 总数，不是一个样本：4999亿，50亿，100亿，以及承诺允许的其他数字。 如果没有承诺，它就会回答 `e.g. (NaN)`一个不是数字的总数。

现在犯每个人都会犯的错误， `>` 哪里 `>=` 本意是:

```ts
import type { Implementation } from "@emyann/tsverify"
import type { ShippingSpec } from "./shipping.contract.ts"

export const shipping: Implementation<ShippingSpec> = (total) => (total > 50 ? 0 : 5)
```

```text
$ npx tsverify
src/shipping.ts
  ✗ shipping  1 counterexample, 1 proved  (177 ms)
    ✗ An order of 50 or more ships free  (freeFrom50, 99 ms)
      e.g. (50)
    ✓ smallOrdersPay  (40 ms)

✗ 1 counterexample
✓ 1 law proved
  1 contract in 1 file · 1.0 s
```

`shipping(50)` 返回 `5`确切数量为50的订单支付运费。反例从来不是假警报：它是假警报 在一个精确的JavaScript模型上找到，所以你的实际代码在它上面中断。该命令退出1，因此是CI步骤或编码 探员，知道任务还没完成。

## 一份说明书，一部分一部分地写

![A spec, part by part: spec of the function type (total: number) => number; requires, what every caller promises; ensures, the laws by name, each given the arguments and then the result, true for every input, written alone or with what it says in words; and its type, which the code takes as Implementation of ShippingSpec.](https://npmcn.com/package/%40emyann/tsverify/media/anatomy-light.svg)

这就是它的全部：法律是一个返回的函数 `true`，从函数中键入，因此它的参数不需要 注释。包装是 `{ says, holds }`，它也表达了它的意思，报告打印了这句话。一条关于异步的定律 代码还可以了解调用如何结束以及它所进行的调用([下面](https://npmcn.com/package/%40emyann/tsverify/#readme-laws-about-what-code-did)）。

## 为什么tsverify

-   **每个输入，而不是样本。** 单元测试检查您写下的输入，属性测试检查一些输入 一千个随机的。一个已证明的定律适用于它们：任意长度的数组，任意字符串，任意数字 `NaN` 和 `-0` 其中，每一个点。
-   **普通的打印稿。** 法律是谓词，规范是值，代码将其作为类型实现。没有新的语言， 没有注释，没有需要解析的注释。 `tsc` 保持代码及其规范的同步，tsverify不会在您的 应用程序。
-   **关于代码作用的法律。** 对于async代码和Effect程序，一个定律读取代码所做的调用，按顺序 他们的论点和结论是：“拒绝撤军没有任何意义”。
-   **你可以采取行动的答案。** 一个反例附带了输入，以及运行时每个调用返回的内容。一项法律 这说明该函数没有任何标记 `vacuous`． 完整的报告显示了该函数可以达到的结果， 每个都有一个到达它的输入。
-   **循环。** 它猜测循环需要的不变量，当它的猜测不够时，你就写一个。
-   **它说它不能做什么。** 它无法读取的代码将在该行被拒绝，取而代之的是要写的内容。它从不治疗 这部分内容不能被解读为正确，而且每一份报告都列出了它所信任的内容。

## 关于代码作用的法律

这里的store是一个接口，它的每个方法都是一个操作：tsverify记录了代码对它的每次调用 它。

```ts
import { type Rejects, spec } from "@emyann/tsverify"

/** Where names are kept. Each method is an operation: tsverify records every call. */
export interface Names {
  readonly get: (id: string) => Rejects<string | undefined, Error>
  readonly set: (id: string, name: string) => Rejects<void, Error>
}

const renameSpec = spec<(names: Names, id: string, name: string) => Rejects<boolean, Error>>({
  ensures: {
    // An empty name never reaches the store: not a single call.
    emptyNameTouchesNothing: (_names, _id, name, _out, calls) => (name === "" ? calls.length === 0 : true),
  },
})
export type RenameSpec = typeof renameSpec
```

```ts
import type { Implementation } from "@emyann/tsverify"
import type { RenameSpec } from "./rename.contract.ts"

export const rename: Implementation<RenameSpec> = async (names, id, name) => {
  const current = await names.get(id)
  if (current === undefined || name === "") return false
  await names.set(id, name)
  return true
}
```

```text
$ npx tsverify src/rename.ts
src/rename.ts
  ✗ rename  1 counterexample  (165 ms)
    ✗ emptyNameTouchesNothing  (95 ms)
      e.g. ({}, "", "")
      where names.get gave ""

✗ 1 counterexample
  1 contract in 1 file · 0.9 s
```

如果名称为空，代码仍然会在说no之前读取存储。先查名字，就证明了这个定律：

修复

```ts
import type { Implementation } from "@emyann/tsverify"
import type { RenameSpec } from "./rename.contract.ts"

export const rename: Implementation<RenameSpec> = async (names, id, name) => {
  if (name === "") return false
  const current = await names.get(id)
  if (current === undefined) return false
  await names.set(id, name)
  return true
}
```

```text
$ npx tsverify src/rename.ts
src/rename.ts
  ✓ rename  1 law  (205 ms)
    ✓ emptyNameTouchesNothing  (136 ms)

✓ 1 law proved
  1 contract in 1 file · 0.9 s
```

效果程序也以同样的方式工作，它们的服务就是操作。[影响项目](https://npmcn.com/package/%40emyann/tsverify/docs/effect.md)）。 [`examples/hono`](https://npmcn.com/package/%40emyann/tsverify/examples/hono) Hono中的用户API是否构建了两次，使用异步处理程序和Effect处理程序，都得到了证明 违反同样的法律。

## 它与性能测试相比如何

在相同的定律上，经过良好调优的属性测试人员经常会发现相同的错误。在 [一个基准](https://npmcn.com/package/%40emyann/tsverify/docs/measurements.md#a-state-machine-an-orders-status) 在一个有10个状态的状态机中植入了17个bug 求解器和属性测试（种子随机输入，偏向于边缘情况）都捕获了相同的10。的区别 在另外7个。求解器解释了每一个：2个是无害的，对每个输入都证明了它们的行为和原来的一样， 还有5个从法律漏洞中溜走，每个都有一个输入。随机测试没有发现它们。

因此，获得的好处不是捕获更多的错误，而是更多的证明而不是样本，以及关于规范的答案 它本身：它的弱点在哪里，法典能达到什么程度，哪些法律没有规定。

## 安装

```sh
npm install --save-dev @emyann/tsverify  # add `effect` for Effect programs
npx tsverify                             # every contract under the current folder
npx tsverify --watch                     # again on every save, until all is proved
```

它需要Node 24.16或更高版本，并自带 `typescript` 和 `z3-solver`． 它读取导入的每个文件 `@emyann/tsverify`，不管它叫什么；这些页面命名一个规范文件 `*.contract.ts`，测试运行器不会将其误认为是测试。

你的 `tsconfig.json` 需要 `strict` (或者至少 `strictNullChecks`)，以及读取包的模块解析 `exports`： `nodenext`， `node16` 或者 `bundler`． 这些例子使用的是：

```json
{
  "compilerOptions": {
    "target": "es2023",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "strict": true,
    "noEmit": true,
    "allowImportingTsExtensions": true,
    "skipLibCheck": true,
    "types": []
  }
}
```

## 答案是什么？

| 判决 | 这意味着什么？ |
| --- | --- |
| `proved` | 规范允许的任何输入都是违法的。 |
| `counterexample` | 有，而且 `e.g.` 给出它，并在运行时返回每个调用。 |
| `vacuous` | 证明了，但是函数的任何结果都不能打破它：定律没有提到函数。 |
| `bounded` | 没有被证明，并且没有输入包含多达4个元素的数组来破坏它。原因说明了什么是阻碍。 |
| `unsupported` | 法律使用了一些不读的东西。理由说的是该写什么。 |
| `unknown` | 解算者没有回答。 |
| `inconsistent` | Tsverify自相矛盾：Tsverify的一个bug，从来不是对你代码的评判。 |

只有当每个law都为时，该命令才为0 `proved`. [读报告](https://npmcn.com/package/%40emyann/tsverify/docs/verdicts.md) 每有判决，怎么办 关于它，还有 `--json` 为工具。

## 了解更多

-   [编写规范](https://npmcn.com/package/%40emyann/tsverify/docs/specs.md)：纯函数，异步代码及其调用，已经存在的代码。
-   [读报告](https://npmcn.com/package/%40emyann/tsverify/docs/verdicts.md)：每一个判决和下一步该做什么；命令及其退出码。
-   [影响项目](https://npmcn.com/package/%40emyann/tsverify/docs/effect.md)：服务作为操作，输入错误，它读取的表单。
-   [循环和证明代码](https://npmcn.com/package/%40emyann/tsverify/docs/proofs.md)：它猜测的不变量，和 `invariant`， `assert` 和前题。
-   [它所读的，拒绝和信任](https://npmcn.com/package/%40emyann/tsverify/docs/reads.md)：它读取的TypeScript，每个拒绝和它的修复， `assume`.
-   [这些例子说明了什么](https://npmcn.com/package/%40emyann/tsverify/docs/measurements.md)：每项测量，除性能测试外。
-   [它是如何工作的](https://npmcn.com/package/%40emyann/tsverify/docs/how-it-works.md)：每个检查的两面，以及代码如何变成Z3。

| 进口 | 为 |
| --- | --- |
| `@emyann/tsverify` | `spec` 和 `Implementation`； `contract` 对于已经存在的代码； `assume`； `invariant`， `assert`， `lemma`;法律规定的类型， `Outcome` 和 `Rejects` |
| `@emyann/tsverify/effect` | `spec` 和 `computation` 对于Effect程序 |
| `@emyann/tsverify/packs/lodash` | tsverify对它所涵盖的lodash函数的假设是什么 |
| `@emyann/tsverify/api` | 作为库的验证器： `loadProject`， `verify`， `openZ3` |
| `@emyann/tsverify/mutants` | 违反你的法律的bug：属性测试旁边的解算器 |

## 状态

原型：版本0.1.0，其API可能在1.0之前更改。这些页面中的每个示例都由其测试运行 包括所示的输出。在你依赖它之前，最重要的是：

-   **实参有自己的类型**代码只在它规定的地方抛出。在程序的边缘，选中 首先是值（比如使用模式）。
-   **你用什么申报？ `assume` 是可信的**没有得到证实，而且每份报告都列出了这一点。
-   **一个循环被带到终点。**
-   **这些例子都是作者自己的**因此，它们显示了tsverify读取和捕获的内容，而不是它如何处理代码 另一个人写道。

[所有的警告](https://npmcn.com/package/%40emyann/tsverify/docs/reads.md#caveats) · [接下来会发生什么](https://npmcn.com/package/%40emyann/tsverify/docs/roadmap.md) · [贡献](https://npmcn.com/package/%40emyann/tsverify/CONTRIBUTING.md)

## 许可证

麻省理工学院

## 常见问题

### @emyann/tsverify 是什么？

为每个输入证明普通TypeScript的规则，或者得到一个打破规则的输入：从源读取，由Z3求解。

### 如何安装 @emyann/tsverify？

在项目目录运行 npm install @emyann/tsverify，或使用 pnpm add / yarn add / bun add。它还提供命令行工具 tsverify，可以用 npx tsverify 直接运行。

### @emyann/tsverify 支持 TypeScript 吗？

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

### @emyann/tsverify 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @emyann/tsverify 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=24\.16\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @emyann/tsverify 可以商用吗？

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

### @emyann/tsverify 还在维护吗？

最新版本 0\.1\.0 发布于 2026/11/01（2 天前）；至今共发布 2 个版本，其中过去 12 个月发布了 2 个；首次发布于 2026/11/01。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

