# @fishka/assertions 中文文档

规范地址：https://npmcn.com/package/%40fishka/assertions/

版本：1\.0\.1
npm 元数据更新时间：2026-01-09T02:39:12.184Z
中文译文更新时间：2026-11-14T20:28:07.901Z
软件包许可：MIT

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

原始 npm 页面：https://www.npmjs.com/package/%40fishka/assertions
原文对照：https://npmcn.com/package/%40fishka/assertions/?lang=original
源码仓库：https://gitea.com/fishka/assertions

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

- 包体积（解压后）：192 kB，84 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=10
- 运行依赖：0 个
- 首次发布：2026-01-06T02:34:03.333Z
- 最新版本发布：2026-01-09T02:39:12.007Z
- 近 30 天下载量：6188（2026-10-15 至 2026-11-13，npm 官方下载统计）
- 已发布版本数：2（近 12 个月 2 个）

## 中文简介

类型脚本的测试和验证

## 安装

```sh
npm install @fishka/assertions
```

也可以使用：`pnpm add @fishka/assertions`、`yarn add @fishka/assertions`、`bun add @fishka/assertions`

## README

# @fishka/ servitions (英语)

TypeScript 的安全运行时间断言和验证,并附有编译时间验证.

## 特征

-   **类型安全** - 完整的脚本支持 `asserts` 类型警卫
-   **编译时间检查** - 对象评估确保所有字段都覆盖
-   **两种模式** - 请求(推出错误)或验证(返回错误字符串)
-   **零依赖关系** - 轻而自足
-   **可编曲** - 从简单的构件中建造复杂的验证器
-   **丰富的制约因素** - 矩阵长度、独特性、字符串模式、跨字段验证
-   **可动摇的树** - 只导入你需要的东西

## 安装

```bash
npm install @fishka/assertions
```

或者说

```bash
yarn add @fishka/assertions
```

## 快速启动

```typescript
import { assertString, assertNumber, assertObject } from '@fishka/assertions';

// Basic type assertions
assertString('hello'); // ✓ passes
assertNumber(42); // ✓ passes
assertString(123); // ✗ throws: "Not a string <number:123>"

// Object validation with compile-time type safety
interface User {
  name: string;
  age: number;
}

const userAssertion: ObjectAssertion<User> = {
  name: assertString,
  age: assertNumber,
};

const data: unknown = { name: 'Alice', age: 30 };
assertObject(data, userAssertion);
// TypeScript now knows `data` is User type
console.log(data.name); // ✓ type-safe
```

## 核心概念

### 请求与验证

**请求** 扔出错误并使用类型脚本 `asserts` 类型守护 :

```typescript
import { assertString, assertObject } from '@fishka/assertions';

function processUser(data: unknown) {
  assertObject(data, userAssertion); // throws if invalid
  // TypeScript now knows data is User
  console.log(data.name.toUpperCase());
}
```

**验证** 不丢弃返回错误消息 :

```typescript
import { validateObject } from '@fishka/assertions';

function validateUserInput(input: unknown) {
  const error = validateObject(input, userAssertion);
  if (error) {
    return { success: false, error };
  }
  return { success: true, data: input }; // TypeScript knows input is User
}
```

使用 **主张** 用于内部一致性检查。 使用 **审定** 用于用户输入或需要优雅处理错误.

## 常用案例

### 1\. 典型类型

```typescript
import { assertString, assertNumber, assertBoolean, assertDate, assertNonNullable } from '@fishka/assertions';

assertString('hello'); // ✓
assertNumber(42); // ✓
assertBoolean(true); // ✓
assertDate(new Date()); // ✓
assertNonNullable('value'); // ✓
assertNonNullable(null); // ✗ throws: "Value is null"
```

### 2\. 格式请求

```typescript
import { assertUuid, assertEmail, assertHexString } from '@fishka/assertions';

assertUuid('550e8400-e29b-41d4-a716-446655440000'); // ✓
assertEmail('user@example.com'); // ✓
assertHexString('a1b2c3'); // ✓
```

### 3\. 对象验证

#### 基本对象

```typescript
import { assertObject, type ObjectAssertion } from '@fishka/assertions';

interface User {
  name: string;
  age: number;
  email: string;
}

const userAssertion: ObjectAssertion<User> = {
  name: assertString,
  age: assertNumber,
  email: assertEmail,
};

assertObject({ name: 'Alice', age: 30, email: 'alice@example.com' }, userAssertion);
```

#### 可选字段

```typescript
import { undefinedOr, nullOr } from '@fishka/assertions';

interface Profile {
  name: string;
  nickname?: string; // optional
  bio: string | null; // nullable
}

const profileAssertion: ObjectAssertion<Profile> = {
  name: assertString,
  nickname: undefinedOr(assertString), // allows undefined
  bio: nullOr(assertString), // allows null
};
```

#### 嵌入对象

```typescript
interface Address {
  street: string;
  city: string;
}

interface User {
  name: string;
  address: Address;
}

const addressAssertion: ObjectAssertion<Address> = {
  street: assertString,
  city: assertString,
};

const userAssertion: ObjectAssertion<User> = {
  name: assertString,
  address: addressAssertion, // nested object
};
```

#### 跨外地验证

```typescript
const userAssertion: ObjectAssertion<User> = {
  name: assertString,
  age: assertNumber,
  email: assertEmail,
  $o: user => {
    // Custom validation after all fields pass
    assertTruthy(user.age >= 18, 'User must be 18 or older');
  },
};
```

#### 严格模式( 无额外字段)

```typescript
assertObject(
  { name: 'Alice', age: 30, unknownField: 'oops' },
  userAssertion,
  undefined,
  { failOnUnknownFields: true }, // ✗ throws: "property can't be checked: unknownField"
);
```

### 4\. 阵列验证

#### 基本阵列

```typescript
import { assertArray, arrayAssertion } from '@fishka/assertions';

assertArray(['a', 'b', 'c'], assertString); // ✓
assertArray([1, 2, 3], assertNumber); // ✓

// Or create reusable array assertion
const stringArrayAssertion = arrayAssertion(assertString);
assertArray(['x', 'y'], stringArrayAssertion);
```

#### 矩阵限制

```typescript
// Length constraints
assertArray([1, 2, 3], assertNumber, { minLength: 2, maxLength: 5 }); // ✓
assertArray([1], assertNumber, { minLength: 2 }); // ✗ throws

// Uniqueness check
assertArray(
  ['a', 'b', 'c'],
  assertString,
  { uniqueByIdentity: v => v }, // ✓
);

assertArray(
  ['a', 'a', 'b'],
  assertString,
  { uniqueByIdentity: v => v }, // ✗ throws: "array contains non-unique elements"
);
```

#### 对象中的矩阵

```typescript
import { undefinedOr } from '@fishka/assertions';

interface User {
  name: string;
  tags?: string[];
}

const userAssertion: ObjectAssertion<User> = {
  name: assertString,
  tags: undefinedOr(arrayAssertion(assertString)),
};
```

### 5\. 记录(词典)验证

```typescript
import { assertRecord, recordAssertion } from '@fishka/assertions';

// Record<string, number>
assertRecord({ a: 1, b: 2, c: 3 }, assertNumber); // ✓

// With key validation
assertRecord(
  { 'user@example.com': 'Alice' },
  assertString,
  { keyAssertion: assertEmail }, // validates keys are emails
);

// Ensure key matches field in value
assertRecord(
  {
    user123: { id: 'user123', name: 'Alice' },
    user456: { id: 'user456', name: 'Bob' },
  },
  objectAssertion({ id: assertString, name: assertString }),
  { keyField: 'id' }, // ensures record key === value.id
);
```

### 6\. 自定义请求

#### 已检查的自定义请求

```typescript
import { $a } from '@fishka/assertions';

// Create custom assertion with type checking
const assertPositive = $a<number>(value => value > 0, 'Must be positive');

assertPositive(42); // ✓
assertPositive(-5); // ✗ throws: "Check is failed: Must be positive"
```

#### 自定义请求

```typescript
import { $u } from '@fishka/assertions';

const assertValidDate = $u(value => value instanceof Date && !isNaN(value.getTime()), 'Must be a valid Date');

assertValidDate(new Date()); // ✓
assertValidDate(new Date('invalid')); // ✗ throws
```

#### 字符串限制

```typescript
import { stringAssertion } from '@fishka/assertions';

const passwordAssertion = stringAssertion({
  minLength: 8,
  maxLength: 64,
});

passwordAssertion('short'); // ✗ throws: "length is too small: 5 < 8"
passwordAssertion('validpassword'); // ✓
```

### 7\. 用户输入验证(不投掷)

```typescript
import { validateObject } from '@fishka/assertions';

function handleFormSubmit(formData: unknown) {
  const error = validateObject(formData, userAssertion);

  if (error) {
    // Show error to user
    console.error('Validation failed:', error);
    return { success: false, error };
  }

  // formData is now typed as User
  saveUser(formData);
  return { success: true };
}
```

### 8\. API 验证

#### 备选案文1:API 请求验证(Express.js Server)

验证服务器侧收到的 HTTP 请求 :

```typescript
import express from 'express';
import { validateObject, assertObject, type ObjectAssertion } from '@fishka/assertions';

interface CreateUserRequest {
  name: string;
  email: string;
  age: number;
  tags?: string[];
}

const createUserRequestAssertion: ObjectAssertion<CreateUserRequest> = {
  name: assertString,
  email: assertEmail,
  age: assertNumber,
  tags: undefinedOr(arrayAssertion(assertString)),
};

const app = express();
app.use(express.json());

// POST /users - Create new user
app.post('/users', (req, res) => {
  // Validate request body
  const error = validateObject(req.body, createUserRequestAssertion);
  if (error) {
    return res.status(400).json({ success: false, error: `Invalid request: ${error}` });
  }
  // req.body is now typed as CreateUserRequest
  const user = createUser(req.body);
  res.status(201).json({ success: true, data: user });
});

// GET /users/:id - Get user by ID
app.get('/users/:id', (req, res) => {
  const userId = req.params.id;
  // Validate UUID format
  assertUuid(userId, 'userId');
  const user = getUserById(userId);
  res.json({ success: true, data: user });
});
```

#### 备选案文2:API 响应验证(客户端)

验证客户端的API响应,以确保类型安全 :

```typescript
interface ApiResponse<T> {
  success: boolean;
  data: T;
  error?: string;
}

interface User {
  id: string;
  name: string;
  email: string;
  createdAt: string;
}

interface UsersListResponse {
  users: User[];
  meta: {
    total: number;
    page: number;
    pageSize: number;
  };
}

// Define response assertion
const userAssertion: ObjectAssertion<User> = {
  id: assertUuid,
  name: assertString,
  email: assertEmail,
  createdAt: assertString,
};

const usersListResponseAssertion: ObjectAssertion<UsersListResponse> = {
  users: arrayAssertion(userAssertion),
  meta: {
    total: assertNumber,
    page: assertNumber,
    pageSize: assertNumber,
  },
};

// Client function to fetch users
async function fetchUsers(page: number = 1): Promise<UsersListResponse> {
  const response = await fetch(`/api/users?page=${page}`);
  const data: unknown = await response.json();

  // Validate response structure
  assertObject(data, usersListResponseAssertion);

  // Now data is fully typed as UsersListResponse!
  return data;
}

// Usage
const { users, meta } = await fetchUsers(1);
console.log(`Loaded ${users.length} of ${meta.total} users`);
```

#### 奖金:快递的中间软件模式

创建可重复使用的验证中间软件 :

```typescript
import { Request, Response, NextFunction } from 'express';
import { validateObject, type ObjectAssertion } from '@fishka/assertions';

// Generic validation middleware
function validateBody<T>(assertion: ObjectAssertion<T>) {
  return (req: Request, res: Response, next: NextFunction) => {
    const error = validateObject(req.body, assertion);

    if (error) {
      return res.status(400).json({
        success: false,
        error: `Validation failed: ${error}`,
      });
    }

    // Body is valid, continue to route handler
    next();
  };
}

// Use the middleware
app.post('/users', validateBody(createUserRequestAssertion), (req, res) => {
  // req.body is typed and validated
  const user = createUser(req.body);
  res.status(201).json({ success: true, data: user });
});
```

## API 参考

### 原始请求

```typescript
assertString(value: unknown, context?: string): asserts value is string
assertNumber(value: unknown, context?: string): asserts value is number
assertBoolean(value: unknown, context?: string): asserts value is boolean
assertDate(value: unknown, context?: string): asserts value is Date
assertNonNullable<T>(value: T, context?: string): asserts value is NonNullable<T>
```

### 格式请求

```typescript
assertUuid(value: unknown, context?: string): asserts value is string
assertHexString(value: unknown, context?: string): asserts value is string
assertEmail(value: unknown, context?: string): asserts value is string
```

### 结构请求

```typescript
assertObject<T>(
  value: unknown,
  assertion: ObjectAssertion<T>,
  context?: string,
  constraints?: { failOnUnknownFields?: boolean; allowedUnknownFieldNames?: string[] }
): asserts value is T

assertArray<T>(
  value: unknown,
  elementAssertion: Assertion<T>,
  constraints?: { minLength?: number; maxLength?: number; uniqueByIdentity?: (el: T) => string },
  context?: string
): asserts value is Array<T>

assertRecord<T>(
  value: unknown,
  valueAssertion: Assertion<T>,
  constraints?: { keyAssertion?: (key: string) => void; keyField?: string },
  context?: string
): asserts value is Record<string, T>
```

### 工厂功能

```typescript
arrayAssertion<T>(elementAssertion: Assertion<T>, constraints?): ValueAssertion<Array<T>>
recordAssertion<T>(valueAssertion: Assertion<T>, constraints?): ValueAssertion<Record<string, T>>
objectAssertion<T>(objAssertion: ObjectAssertion<T>): ValueAssertion<T>

$a<T>(checkFn: (value: T) => boolean, errorMsg?: string): ValueAssertion<T>
$u(checkFn: (value: unknown) => boolean, errorMsg?: string): ValueAssertion<unknown>

undefinedOr<T>(assertion: Assertion<T>): Assertion<T | undefined>
nullOr<T>(assertion: Assertion<T>): Assertion<T | null>
valueOr<T>(expectedValue: T, orAssertion: Assertion<T>): Assertion<T>

stringAssertion(constraints: { minLength?: number; maxLength?: number }): ValueAssertion<string>
```

### 校验函数( 非抛出)

```typescript
validateObject<T>(value: unknown, assertion: ObjectAssertion<T>, context?, constraints?): string | undefined
validateArray<T>(value: unknown, elementAssertion: Assertion<T>, constraints?, context?): string | undefined
validateRecord<T>(value: unknown, valueAssertion: Assertion<T>, constraints?, context?): string | undefined
```

### 类型预测( 检查)

```typescript
isString(value: unknown): value is string
isNumber(value: unknown): value is number
isBoolean(value: unknown): value is boolean
isDate(value: unknown): value is Date
isUuid(value: unknown): value is string
isHexString(value: unknown): value is string
isEmail(value: unknown, constraints?): value is string
isNonNullable<T>(value: T): value is NonNullable<T>
```

### 核心职能

```typescript
assertTruthy(value: unknown, error?: string | (() => string)): asserts value
fail(error?: string | (() => string)): never  // throws assertion error
```

## 高级特性

### 上下文传播出错

错误自动包含到失败字段的全部路径 :

```typescript
const data = {
  user: {
    profile: {
      age: 'twenty', // wrong type
    },
  },
};

assertObject(data, userAssertion, 'apiResponse');
// throws: "apiResponse.user.profile.age: Not a number <string:twenty>"
```

### 类型脚本编译-时间安全

ObjectAsertion 正在编译-时间验证 - TypeScript 确保所有字段覆盖:

```typescript
interface User {
  name: string;
  age: number;
  email: string;
}

const userAssertion: ObjectAssertion<User> = {
  name: assertString,
  age: assertNumber,
  // ✗ TypeScript error: Property 'email' is missing
};
```

### 自定义出错厂

覆盖所有断言的默认错误厂:

```typescript
import { setDefaultAssertionErrorFactory } from '@fishka/assertions';

setDefaultAssertionErrorFactory((message: string) => {
  return new CustomError(message);
});
```

## 常见模式

### 正在验证配置文件

```typescript
interface AppConfig {
  port: number;
  host: string;
  database: {
    url: string;
    poolSize: number;
  };
  features: string[];
}

const configAssertion: ObjectAssertion<AppConfig> = {
  port: assertNumber,
  host: assertString,
  database: {
    url: assertString,
    poolSize: assertNumber,
  },
  features: arrayAssertion(assertString),
};

const config: unknown = JSON.parse(configFileContents);
assertObject(config, configAssertion);
// config is now fully typed and validated
```

### 表格验证

```typescript
function validateLoginForm(formData: FormData): { error?: string; data?: LoginData } {
  const input = {
    email: formData.get('email'),
    password: formData.get('password'),
  };

  const error = validateObject(input, loginAssertion);
  if (error) {
    return { error };
  }

  return { data: input };
}
```

### 运行时间类型缩小

```typescript
function processValue(value: unknown) {
  if (isString(value)) {
    // TypeScript knows value is string
    console.log(value.toUpperCase());
  } else if (isNumber(value)) {
    // TypeScript knows value is number
    console.log(value.toFixed(2));
  }
}
```

## 为什么?

-   **类型安全**: 存储类型Script的编译时间和运行时间安全类型系统
-   **开发者经验**: 以完整上下文路径清除错误消息
-   **无依赖**: 没有外部依赖, 最小捆绑大小
-   **弹性**: 在投掷断言或返回错误消息之间选择
-   **可编曲**: 从简单的构件构建复杂的验证器
-   **作战测试**: 附有111+测试的综合测试套件

## 许可证

麻省理工学院

## 捐款

欢迎提出问题和拉动请求 [https://gitea.com/fishka/assertions (英语)](https://gitea.com/fishka/assertions)

## 常见问题

### @fishka/assertions 是什么？

类型脚本的测试和验证

### 如何安装 @fishka/assertions？

在项目目录运行 npm install @fishka/assertions，或使用 pnpm add / yarn add / bun add。

### @fishka/assertions 支持 TypeScript 吗？

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

### @fishka/assertions 支持 ESM 还是 CommonJS？

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

### @fishka/assertions 需要什么版本的 Node\.js？

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

### @fishka/assertions 可以商用吗？

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

### @fishka/assertions 的下载量有多少？

根据 npm 官方下载统计，@fishka/assertions 在 2026/10/15 – 2026/11/13 这 30 天内被下载 6,188 次，日均约 206 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @fishka/assertions 还在维护吗？

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

