阿里·斯克马
一个 Typescript 验证器和 schema 构建器,可以编译到其他语言. 两人的灵感很多 类型框 财务报告和审定财务报表 佐德语Name 当设计此库时。 此库也支持 标准大纲 意思是它可以和任何接受标准schema的第三方库一起使用.
在盖子下,这个图书馆构造 阿里类型定义这些定义可以传给 阿里·CLI 为Arri支持的任何客户端语言生成代码。 最后,这个图书馆还有一个 JIT 编译器 产生比Zod快100倍的预编译验证器.
项目哲学
该项目的目标如下:
- 便携式类型定义
- 高性能验证、解析和序列化
- 分析错误和序列化错误的一致错误报告
我并不想支持Typescript系统的所有功能, 甚至是JSON的所有可能的表示。 目标是通过这个库定义的数据模型可以被多种编程语言用作真理的来源. JSON和Typescript必须加以限制才能做到这一点.
加入RFC 8927
最初,这个图书馆是用来为 Json 类型定义然而,随着时间的推移,对内部计划的一些部分进行了修改,以便更好地实现Arri RPC的目标。 其中一些修改包括:
- 添加对 64 位整数的支持
- 替换
additionalProperties字段为strict以默认允许额外属性. - 限制
ref仅用于递归引用.
目录
安装
# npm
npm install @arrirpc/schema
# pnpm
pnpm install @arrirpc/schema
基本示例
import { a } from '@arrirpc/schema';
const User = a.object({
id: a.string(),
name: a.string(),
});
type User = a.infer<typeof User>;
// returns ResultSuccess<User>
a.parse(User, `{"id": "1", "name": "John Doe"}`);
// returns ResultFailure
a.parse(User, `{"id": "1", "name": null}`);
// returns true
a.validate(User, { id: '1', name: 'John Doe' });
// returns false
a.validate(User, { id: '1', name: null });
// outputs valid json
a.serialize(User, { id: '1', name: 'John Doe' });
// JIT compiled validator (faster but server-side only)
const $$User = a.compile(User);
$$User.validate({ id: '1', name: 'John Doe' });
$$User.parse(`{"id": "1", "name": "John Doe"}`);
$$User.serialize({ id: '1', name: 'John Doe' });
使用@ arrirpc/ 服务器
见 这里 详细情况.
import { a } from '@arrirpc/schema';
import { defineRpc } from '@arrirpc/server';
export default defineRpc({
params: a.object({
name: a.string(),
}),
response: a.object({
message: a.string(),
}),
handler({ params }) {
// can now access params.name here
return {
message: `Hello ${params.name}`,
};
},
});
汇编到其他语言
使用此库定义的所有计划可以编译为其它语言 阿里·CLI。 。 。 .
安装 ARRI CI
# npm
npm i --save-dev arri
# pnpm
pnpm i --save-dev arri
创建您的 Arri 配置
import { defineConfig, generators } from 'arri';
export default defineConfig({
generators: [
// add your generators here
generators.rustClient({
// options
}),
generators.dartClient({
// options
}),
],
});
创建并导出您的外观
使用 createAppDefinition 帮助您为 ARRI CLI 输出您的计划 .
// definitions.ts
import { createAppDefinition } from 'arri';
import { a } from '@arrirpc/schema';
const User = a.object('User', {
id: a.string(),
name: a.optional(string()),
email: a.nullable(a.string()),
createdAt: a.timestamp({ description: 'When the user was created' }),
updatedAt: a.timestamp(),
});
export default createAppDefinition({
definitions: {
User,
},
});
运行代码生成器
# npm
npx arri codegen ./definitions.ts
# pnpm
pnpm arri codegen ./definitions.ts
还有你的功劳 现在,每当您的计划更新时,您都可以重运行此命令 .
示例输出
// dart output
class User {
final String id;
final String? name;
final String? email;
/// when the user was created
final DateTime createdAt;
final DateTime updatedAt;
const User({
required this.id,
this.name,
required this.email,
required this.createdAt,
required this.updatedAt,
});
// implementation details
}
// rust output
pub struct User {
id: String,
name: String,
name: Option<String>,
email: Option<String>,
// when the user was created
created_at: DateTime<FixedOffset>,
updated_at: DateTime<FixedOffset>,
}
impl ArriModel for User {
// implementation details
}
// kotlin output
data class User(
val id: String,
val name: String?,
val email: String? = null,
/**
* When the user was created
*/
val createdAt: Instant,
val updatedAt: Instance,
) {
// implementation details
}
见 这里 用于列出所有官方支持的语言生成器.
可树动导入
对于关心捆绑大小的人,您可以使用Arri的可选模块导入语法. 这使得捆绑者可以在建设时从JS捆绑中去除未使用的Arri功能. 您也可以在您的代码库中使用 灌溉/预模进口 规则.
使用模块导入语法Arri的捆绑大小可小到4kb,取决于您导入的函数数.
// tree-shakeable (no `a` prefix)
import { string, boolean, object } from '@arrirpc/schema';
const User = object({
id: string(),
name: string(),
isAdmin: boolean(),
});
// tree-shakeable (with `a` prefix)
import * as a from '@arrirpc/schema';
const User = a.object({
id: a.string(),
name: a.string(),
isAdmin: a.boolean(),
});
// NOT tree-shakeable
import { a } from '@arrirpc/schema';
const User = a.object({
id: a.string(),
name: a.string(),
isAdmin: a.boolean(),
});
点击 这里 看Arri Schema的捆绑尺寸 和生态系统的其余部分相比.
为什么这不是默认?
只是个人偏好。 我发现手动导入个人函数是一种糟糕的开发者体验.
// not a fan
import { foo, bar, baz } from 'foo';
此外,明确出口 a 命名空间是指当我输入时 a.{something} tS语言服务器会自动完成可用的功能,即使我还没有导入 @arrirpc/schema 还没有。 如果我们没有那么明确的出口, 那你就不会得到自动完成 a.{something} 直到您添加 import * as a 线条到您的文件.
// this has to be added before you get autocomplete for `a.{whatever}`
import * as a from '@arrirpc/schema';
但是,我明白,保持小的捆绑尺寸可能很重要 这就是为什么我允许两种选择:
// if you care about bundle size use one of these two
import * as a from '@arrirpc/schema';
import { string, object, etc } from '@arrirpc/schema';
// if you aren't as particular about bundle sizes then use this
import { a } from '@arrirpc/schema';
支持类型
初级
| 阿里·斯克马 | 字体 | Arri 类型定义 |
|---|---|---|
| a. 任何( ) | 任意 | {} |
| a. 字符串( ) | 字符串 | {"类型":"字符串"} |
| a. boolean () (中文(简体) ) | 布尔 | {"类型":"boole"} |
| a. 时间戳( ) | 日期 | {"类型":"时间戳"} |
| a. float32( ) | 编号 | {"类型":"float32"} |
| a. float64 () (中文(简体) ) | 编号 | {"类型":"float64"} |
| a.int8 () (中文(简体) ) | 编号 | {"类型":"int8"} |
| a.int16 () (中文(简体) ) | 编号 | {"类型":"int16"} |
| a.int32() (中文(简体) ) | 编号 | {"类型":"int32"} |
| a.int64() (中文(简体) ) | 大点 | {"类型":"int64"} |
| a. uint8( ) | 编号 | {"类型":"int8"} |
| a.int16 () (中文(简体) ) | 编号 | {"类型":"int16"} |
| a.int32() (中文(简体) ) | 编号 | {"类型":"int32"} |
| a.int64() (中文(简体) ) | 大点 | {"类型":"int64"} |
数字
Enum 计划允许您指定一个预定义的已接受字符串列表
使用量
const Status = a.enumerator(['ACTIVE', 'INACTIVE', 'UNKNOWN']);
type Status = a.infer<typeof Status>; // "ACTIVE" | "INACTIVE" | "UNKNOWN";
a.validate(Status, 'BLAH'); // false
a.validate(Status, 'ACTIVE'); // true
输出的 ATD
{
"enum": ["ACTIVE", "INACTIVE", "UNKNOWN"]
}
矩阵/ 列表
使用量
const MyList = a.array(a.string());
type MyList = a.infer<typeof MyList>; // string[];
a.validate(MyList, [1, 2]); // false
a.validate(MyList, ['hello', 'world']); // true
输出的 ATD
{
"elements": {
"type": "string"
}
}
对象
使用量
const User = a.object({
id: a.string(),
email: a.string(),
created: a.timestamp(),
});
type User = a.infer<typeof User>; // { id: string; email: string; created: Date; }
a.validate(User, {
id: '1',
email: '[email protected]',
created: new Date(),
}); // true
a.validate(User, {
id: '1',
email: null,
created: new Date(),
}); // false
输出的 ATD
{
"properties": {
"id": {
"type": "string"
},
"email": {
"type": "string"
},
"created": {
"type": "timestamp"
}
}
}
严格模式
默认情况下, @arrirpc/schema 在验证对象时会忽略并删除任何额外的属性 。 如果您想要在存在额外属性时验证失败, 请修改 strict 选项.
const UserStrict = a.object(
{
id: a.string(),
name: a.string(),
created: a.timestamp(),
},
{
strict: true,
},
);
a.parse(UserStrict, {
id: '1',
name: 'johndoe',
created: new Date(),
bio: 'my name is joe',
}); // fails parsing because of the additional field "bio"
输出的 ATD
{
"properties": {
"id": {
"type": "string"
},
"email": {
"type": "string"
},
"created": {
"type": "timestamp"
}
},
"strict": true
}
记录/地图
使用量
const R = a.record(a.boolean());
type R = a.infer<typeof R>; // Record<string, boolean>
a.validate(R, {
hello: true,
world: false,
}); // true;
a.validate(R, {
hello: 'world',
}); // false;
输出的 ATD
{
"values": {
"type": "boolean"
}
}
歧视工会
使用量
const Shape = a.discriminator('type', {
RECTANGLE: a.object({
width: a.float32(),
height: a.float32(),
}),
CIRCLE: a.object({
radius: a.float32(),
}),
});
type Shape = a.infer<typeof Shape>; // { type: "RECTANGLE"; width: number; height: number; } | { type: "CIRCLE"; radius: number; }
// Infer specific sub types of the union
type ShapeTypeRectangle = a.inferSubType<Shape, 'type', 'RECTANGLE'>; // { type "RECTANGLE"; width: number; height: number; };
type ShapeTypeCircle = a.inferSubType<Shape, 'type', 'CIRCLE'>; // { type "CIRCLE"; radius: number; }
a.validate(Shape, {
type: 'RECTANGLE',
width: 1,
height: 1.5,
}); // true
a.validate(Shape, {
type: 'CIRCLE',
radius: 5,
}); // true
a.validate(Shape, {
type: 'CIRCLE',
width: 1,
height: 1.5,
}); // false
输出的 ATD
{
"discriminator": "type",
"mapping": {
"RECTANGLE": {
"properties": {
"width": {
"type": "float32"
},
"height": {
"type": "float32"
}
}
},
"CIRCLE": {
"properties": {
"radius": {
"type": "float32"
}
}
}
}
}
递归类型
您可以使用 a.recursive 帮手 此函数接受另一个输出对象计或歧义计的函数.
需要注意的一点是,类型推论对递归式计划并不正确. 为了满足 Typescription, 您需要定义类型, 然后把它传递给函数作为通用 .
此外,建议为任何递归计划确定标识。 如果未指定 arri 将自动生成 .
如果一些 TS 向导知道如何使类型推论自动为这些递归性计划工作,那么尽管我担心需要对现有类型系统进行重大重构,却可以随意打开一个PR.
使用量
// the recursive type must be defined first
type BinaryTree = {
left: BinaryTree | null;
right: BinaryTree | null;
};
// pass the type to the helper
const BinaryTree = a.recursive<BinaryTree>(
(self) =>
// the resulting schema must be an object or discriminator
// it also must match the type you pass into the generic parameter
// or TS will yell at you
a.object({
left: a.nullable(self),
right: a.nullable(self),
}),
{
id: 'BinaryTree',
},
);
a.validate(BinaryTree, {
left: {
left: null,
right: {
left: null,
right: null,
},
},
right: null,
}); // true
a.validate(BinaryTree, {
left: {
left: null,
right: {
left: true,
right: null,
},
},
right: null,
}); // false
输出的 ATD
{
"properties": {
"left": {
"ref": "BinaryTree",
"nullable": true
},
"right": {
"ref": "BinaryTree",
"nullable": true
}
},
"metadata": {
"id": "BinaryTree"
}
}
修改器
可选
使用 a.optional() 使对象字段具有可选性.
const User = a.object({
id: a.string(),
email: a.optional(a.string()),
date: a.timestamp();
})
/**
* Resulting type
* {
* id: string;
* email?: string | undefined;
* date: Date;
* }
*/
输出的 ATD
{
"properties": {
"id": {
"type": "string"
},
"date": {
"type": "timestamp"
}
},
"optionalProperties": {
"email": {
"type": "string"
}
}
}
无效
使用 a.nullable() 使特定类型无效
const name = a.nullable(a.string());
/**
* Resulting type
* string | null
*/
输出的 ATD
{
"type": "string",
"nullable": true
}
无法定义
这类似于 a.optional() 但是,在初始化对象时,仍然需要按键.
const Foo = a.object({
foo: a.undefinable(a.string()),
});
type Foo = a.infer<typeof Foo>;
const fooInstance: Foo = {
// this field must still be present
// while with a.optional() we could omit the key
foo: undefined,
};
就解析和验证而言,此函数与 a.optional()。 。 。 .
const User = a.object({
id: a.string(),
email: a.undefinable(a.string()),
date: a.timestamp();
})
/**
* Resulting type (Notice how the email key is still required)
* {
* id: string;
* email: string | undefined;
* date: Date;
* }
*/
输出的 ATD
{
"properties": {
"id": {
"type": "string"
},
"date": {
"type": "timestamp"
}
},
"optionalProperties": {
"email": {
"type": "string"
}
}
}
克隆
复制另一个图案而不复制 它的元数据使用 a.clone() 帮助者
const A = a.object(
{
a: a.string(),
b: a.float32(),
},
{ id: 'A' },
);
console.log(A.metadata.id); // "A"
const B = a.clone(A);
console.log(B.metadata.id); // undefined
扩展
以 a.extend() 帮手.
const A = a.object({
a: a.string(),
b: a.float32(),
});
// { a: string; b: number; }
const B = a.object({
c: a.timestamp(),
});
// { c: Date }
const C = a.extend(A, B);
// { a: string; b: number; c: Date }
奥米
使用 a.omit() 创建新对象计划,并删除某些属性
const A = a.object({
a: a.string(),
b: a.float32(),
});
// { a: string; b: number; }
const B = a.omit(A, ['a']);
// { b: number; }
选择
使用 a.pick() 创建新对象计划,其中包含来自父对象的属性子集
const A = a.object({
a: a.string(),
b: a.float32(),
c: a.timestamp(),
});
// { a: string; b: number; c: Date; }
const B = a.pick(A, ['a', 'c']);
// { a: string; c: Date; }
部分
使用 a.partial() 以创建新的对象方案,使母方案的所有属性都具有可选性.
const A = a.object({
a: a.string(),
b: a.float32(),
c: a.timestamp(),
});
// { a: string; b: number; c: Date; }
const B = a.partial(A);
// { a: string | undefined; b: number | undefined; c: Date | undefined; }
公用事业
校验
打电话 a.validate() 以验证一个输入 相对于 arri 计划。 这种方法也起到类型保护的作用,所以任何 any 或者说 unknown 通过验证的类型会自动为已验证的字段获得自动完成
const User = a.object({
id: a.string(),
name: a.string(),
});
a.validate(User, true); // false
a.validate(User, { id: '1', name: 'john doe' }); // true
if (a.validate(User, someInput)) {
console.log(someInput.id); // intellisense works here
}
解析
打电话 a.parse() 把JSON的绳子和Arri的图案联系起来 它也会处理正常对象的剖析.
const User = a.object({
id: a.string(),
name: a.string(),
});
// returns Result<User>
const result = a.parse(User, jsonString);
if (result.success) {
// something when wrong with parsing
console.log(result.errors);
} else {
// parsing was successful
console.log(result.value);
}
解析不安全
备用版本改为 parse() 将投出一个 ValidationException 如果解析失败.
const User = a.object({
id: a.string(),
name: a.string(),
});
// can throw an error
const result = a.parseUnsafe(User, jsonString);
console.log(result);
胁迫
a.coerce() 将尝试将输入转换为正确的类型。 回返a Result<T>
const A = a.object({
a: a.string(),
b: a.boolean(),
c: a.float32(),
});
a.coerce(A, {
a: '1',
b: 'true',
c: '500.24',
});
// { success: true, value: { a: '1', b: true, c: 500.24 } };
胁迫不安全
a.coerceUnsafe() 是替代 a.coerce() 如果胁迫失败, 就会出错
const A = a.object({
a: a.string(),
b: a.boolean(),
c: a.float32(),
});
a.coerceUnsafe(A, someInput); // returns T but can throw an error
序列化
a.serialize() 将输入并将其序列化为有效的 JSON 字符串。 此返回 Result<string>
const User = a.object({
id: a.string(),
name: a.string(),
});
const result = a.serialize(User, { id: '1', name: 'john doe' });
if (result.success) {
console.log(result.value);
// '{"id":"1","name":"john doe"}''
}
注意此函数不验证输入 。 因此,如果您正在以任何或未知类型传递到此函数,建议您先验证它.
序列化不安全
a.serializeUnsafe() 是替代 a.serialize() 返回 JSON 字符串,但可以投出错误.
const User = a.object({
id: a.string(),
name: a.string(),
});
const result = a.serialize(User, { id: '1', name: 'john doe' }); // might throw an error
// '{"id":"1","name":"john doe"}''
注意此函数不验证输入 。 因此,如果您正在以任何或未知类型传递到此函数,建议您先验证它.
错误
使用 a.errors() 以获取给定输入的所有验证错误.
const User = a.object({
id: a.string(),
date: a.timestamp(),
});
a.errors(User, { id: 1, date: 'hello world' });
/**
* [
* {
* instancePath: "/id",
* schemaPath: "/properties/id/type",
* message: "Expected string",
* },
* {
* instancePath: "/date",
* schemaPath: "/properties/id/type",
* message: "Expected instanceof Date",
* }
* ]
*
*/
敬杰森·斯克马
使用 a.toJsonSchema() 将 Arri 计划转换为 杰森·斯克马 格式。 这对于:
- 编辑器支持 - YAML/JSON文件使用Arri定义的chemas可以在VS代码,IntelliJ等中自动完成.
- 互通性 - 与使用JSON Schema的工具(OpenAPI、文件生成器等)相结合
- 配置验证 - 定义配置文件的用户可以参考JSON Schema URL进行验证
const User = a.object(
{
id: a.string(),
name: a.string(),
email: a.nullable(a.string()),
role: a.enumerator(['USER', 'ADMIN']),
createdAt: a.timestamp(),
},
{ id: 'User', description: 'A user in the system' },
);
const jsonSchema = a.toJsonSchema(User, {
$id: 'https://example.com/schemas/user.json',
title: 'User Schema',
});
结果 :
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/user.json",
"title": "User Schema",
"type": "object",
"properties": {
"id": { "type": "string" },
"name": { "type": "string" },
"email": { "type": ["string", "null"] },
"role": { "type": "string", "enum": ["USER", "ADMIN"] },
"createdAt": { "type": "string", "format": "date-time" }
},
"required": ["id", "name", "email", "role", "createdAt"]
}
选项
| 选项 | 类型 | 说明 |
|---|---|---|
$id | 字符串 | 这个 $id 用于根图 |
title | 字符串 | 用于根图的名称 |
description | 字符串 | 用于根图的描述 |
$schema | 字符串 | JSON Schema 草稿版本( 默认 : https://json-schema.org/draft/2020-12/schema(中文(简体) ) |
类型绘图
| 阿里·斯克马 | JSON Schema 等效词 |
|---|---|
a.any() | {} (任何一个) |
a.string() | { "type": "string" } |
a.boolean() | { "type": "boolean" } |
a.timestamp() | { "type": "string", "format": "date-time" } |
a.float32/64() | { "type": "number" } |
a.int8/16/32() | { "type": "integer", "minimum": X, "maximum": Y } |
a.uint8/16/32() | { "type": "integer", "minimum": 0, "maximum": Y } |
a.int64/uint64() | { "type": "string", "pattern": "^-?[0-9]+$" } |
a.enumerator() | { "type": "string", "enum": [...] } |
a.array() | { "type": "array", "items": {...} } |
a.object() | { "type": "object", "properties": {...}, "required": [...] } |
a.record() | { "type": "object", "additionalProperties": {...} } |
a.discriminator() | { "oneOf": [...] } 与歧视者 |
a.nullable() | 添加数 "null" 键入数组 |
元数据
元数据在跨语言代码生成时被使用. Arri 计划允许您指定以下元数据字段:
- id - 将在任何arri客户端生成器中用作类型名称
- 说明 - 将在任何生成类型之上添加为说明注释
- is depressed - 将标记任何生成的代码,并标记目标语言的去pression 注释
实例
包含此元数据的计划 :
// metadata object
const BookSchema = a.object(
{
title: a.string(),
author: a.string(),
publishDate: a.timestamp(),
},
{
id: 'Book',
description: 'This is a book',
},
);
将在编码时生成类似类型 .
字体
/**
* This is a book
*/
interface Book {
title: string;
author: string;
publishDate: Date;
}
锈
/// This is a book
struct Book {
title: String,
author: String,
publish_date: DateTime<FixedOffset>
}
达特
/// This is a book
class Book {
final String title;
final String author;
final DateTime publishDate;
const Book({
required this.title,
required this.author,
required this.publishDate,
});
}
科特林语Name
/**
* This is a book
*/
data class Book(
val title: String,
val author: String,
val publishDate: Instant,
)
简称 ID
因为身份证对制作简洁的型号名非常重要. Arri验证还提供了定义物体、歧视者和递归型的识别特征的简写.
// ID will be set to "Book"
const BookSchema = a.object('Book', {
title: a.string(),
author: a.string(),
publishDate: a.timestamp(),
});
// ID will be set to "Message"
const MessageSchema = a.discriminator('Message', 'type', {
TEXT: a.object({
userId: a.string(),
content: a.string(),
}),
IMAGE: a.object({
userId: a.string(),
imageUrl: a.string(),
}),
});
// ID will be set to "BTree"
const BinaryTreeSchema = a.recursive('BTree', (self) =>
a.object({
left: a.nullable(self),
right: a.nullable(self),
}),
);
编译验证器
@arrirpc/schema 具有高性能的JIT编译器,将Arri Schemas转化为高度优化的验证,解析,强制,和串行功能. 汇编的结果还落实了以下内容: 标准大纲 界面,意思是它可以传递到任何接受标准-schema的库中.
const User = a.object({
id: a.string(),
email: a.nullable(a.string()),
created: a.timestamp(),
});
const $$User = a.compile(User);
$$User.validate(someInput);
$$User.parse(someJson);
$$User.parseUnsafe(someJson);
$$User.coerce(someObject);
$$User.coerceUnsafe(someObject);
$$User.serialize({ id: '1', email: null, created: new Date() });
$$User.serializeUnsafe({ id: '1', email: null, created: new Date() });
在大多数情况下,编译的验证器将比标准公用事业快得多。 然而,在汇编计划方面有一些间接费用,因此,理想的做法是每个验证器都汇编一次。 此外,还利用下列方法创建了由此产生的方法: new Function() 因此,它们只能用于你控制的后端服务器等环境中. 它们不会在浏览器环境中工作.
您还可以使用 a.compile 用于代码生成。 通过 true 因为第二个参数将确保编译结果能够使您访问生成的函数机构。 此功能默认已禁用 v0.76.0 以减少不必要的内存使用。 (如果这些大字符串不会被使用的话,就不必随身携带. )
// pass true to the second parameter to get access to the generated function bodies
const $$User = a.compile(User, true);
$$User.compiledCode.validate; // the generated validation code
$$User.compiledCode.parse; // the generated parsing code
$$User.compiledCode.coerce; // the generated coercion code
$$User.compiledCode.serialize; // the generated serialization code
基准
最后更新:2025-06-09T18:41:32.823Z
所有的基准都在我的个人桌面上运行。 您可以查看使用方法 ./基准/弧。 。 。 .
OS - Pop!_OS 22.04 LTS
CPU - AMD Ryzen 9 5900 12-Core Processor
RAM - 32GB
Graphics - AMD® Radeon rx 6900 xt
对象
这些基准采用了以下类型。 在上述各图书馆都建立了等效计划.
interface TestUser {
id: number; // integer,
role: 'standard' | 'admin' | 'moderator';
name: string;
email: string | null;
createdAt: number; // integer
updatedAt: number; // integer
settings:
| {
preferredTheme: 'light' | 'dark' | 'system';
allowNotifications: boolean;
}
| undefined;
recentNotifications: Array<
| {
type: 'POST_LIKE';
userId: string;
postId: string;
}
| {
type: 'POST_COMMENT';
userId: string;
postId: string;
commentText: string;
}
>;
}
对象验证 - 好输入
| 图书馆 | 行动 |
|---|---|
| Arri (编译) | 54 207 983 (中文(简体) ) |
| 类型框(已编译) | 42 991 276 (单位:千美元) |
| 方舟类型 | (单位:千美元) |
| 蒂皮亚 | 28 500 181美元 |
| Arri (编译) - 标准Schema | 19,420,723 (英语) |
| Ajv - JSON Schema (编译) | 11 612 359美元 |
| Ajv - JSON 舍马 | 11,337,540人 (单位:千美元) |
| 佐德/v4区 | 3 080 359 (中文(简体) ) |
| 亚利 | 2,719,630人次 (简体中文) |
| Arri - 标准Schema | 760 346 (韩语) |
| 类型框 | 720,807 (中文(简体) ) |
| 维利博特 | 594 403 (简体中文) |
| 佐德语Name | 463 499 (韩语) |
对象验证 - 坏输入
| 图书馆 | 行动 |
|---|---|
| Arri (编译) | 60 871 312美元) |
| 类型框(已编译) | 47,747,764 (单位:千美元) |
| 蒂皮亚 | 31,195,589个 |
| Arri (编译) - 标准Schema | 8 314 141 (简体中文) |
| Ajv - JSON Schema (编译) | 4 523 107 (中文(简体) ) |
| Ajv - JSON 舍马 | 4 315 160个 |
| 亚利 | 3 992 012个 |
| 类型框 | 891 805 (中文(简体) ) |
| Arri - 标准计划 | 757 845 (韩语) |
| 维利博特 | 496 579 (简体中文) |
| 佐德语Name | 352 986 (简体中文) |
| 方舟类型 | 149 079 (中文(简体) ) |
| 佐德/v4区 | 100 355个 |
对象解析 - 好输入
| 图书馆 | 行动 |
|---|---|
| JSON.parse (英语) | 801 513个 |
| JSON.parse + 类型框(已编译) | 754 820 (中文(简体) ) |
| Arri (编译) | 753,155 (韩语) |
| JSON.parse + 方舟类型 | 748,862 (中文(简体) ) |
| Arri (编译) - 标准Schema | 745,964 (英语) |
| Typia (json.create ValidateParse) (英语) | 726,365 (中文(简体) ) |
| JSON.parse + Zod/v4 (英语) | 555 511 (中文(简体) ) |
| 亚利 | 365,992 (韩语) |
| Arri - 标准Schema | 365 640 (韩语) |
| JSON.parse + 瓦利博特 | 319 333个 |
| JSON.parse + 佐德语 | 285,161 (单位:千美元) |
| JSON.parse + 类型框 | 215 169 (中文(简体) ) |
对象解析 - 坏输入
| 图书馆 | 行动 |
|---|---|
| JSON.parse (英语) | 846 547 (中文(简体) ) |
| Arri (编译) | 779 979 (英语) |
| Arri (编译) - 标准Schema | 718,259个 |
| Typia (json.create ValidateParse) (英语) | 552,892 (韩语) |
| 亚利 | 414 169 (中文(简体) ) |
| Arri (标准计划) | 391,092 (中文(简体) ) |
| JSON.parse + 瓦利博特 | 289 960 (简体中文) |
| JSON.parse + 佐德语 | 220 650 (中文(简体) ) |
| JSON.parse + 方舟类型 | 120 295 (中文(简体) ) |
| JSON.parse + 类型框(已编译) | 99,136 (简体中文) |
| JSON.parse + Zod/v4 (英语) | 80 506 (简体中文) |
| JSON.parse + 类型框 | 77 380美元 (单位:千美元) |
对象序列化
| 图书馆 | 行动 |
|---|---|
| Arri (编译) | 3,984,935人次 (简体中文) |
| Arri (编译) - 验证和序列化 | 3 583 501 (中文(简体) ) |
| 蒂皮亚 | 1,793,677人 (中文(简体) ) |
| JSON 强调 | 1 645 335美元) |
| Typia - 验证和序列化 | 1 540 510美元) |
| 亚利 | 450 120美元 |
强制对象
| 图书馆 | 行动 |
|---|---|
| Arri (编译) | 19,370,221 (韩语) |
| 佐德/v4区 | 2,267,229人次 |
| 亚利 | 738 461 (中文(简体) ) |
| 佐德语Name | 450 168 (中文(简体) ) |
| 类型框 | 403 602 (韩语) |
整数
以下基准衡量每个库以单一整数运行的速度.
导入验证
| 图书馆 | 行动 |
|---|---|
| Arri (编译) | 188 029 099 (中文(简体) ) |
| 类型框(已编译) | 186 595 706 (简体中文) |
| Ajv - JSON Schema (编译) | 182 332 718 (中文(简体) ) |
| Arri (编译) - 标准Schema | 108 964 372 (单位:千美元) |
| Arri - 标准Schema | 108 193 957 (中文(简体) ) |
| 亚利 | 84 093 924 (中文(简体) ) |
| 蒂皮亚 | 58,164,521人 (中文(简体) ) |
| 方舟类型 | 57,782,710 (单位:千美元) |
| Ajv - JSON 舍马 | 50,350,825 (单位:千美元) |
| 类型框 | 46 450 527美元 |
| 维利博特 | 22 202 298 (中文(简体) ) |
| 佐德/v4区 | 18 948 463 (中文(简体) ) |
| 佐德语Name | 1 269 812美元 (单位:千美元) |
Int 验证( 失败输入)
| 图书馆 | 行动 |
|---|---|
| 类型框(已编译) | 188 306 861 (单位:千美元) |
| Arri (编译) | 186 071 486 (中文(简体) ) |
| Ajv - JSON Schema (编译) | 69,251,304 (单位:千美元) |
| 蒂皮亚 | 60 060 635 (中文(简体) ) |
| 类型框 | 44,860,799 (英语) |
| 亚利 | 39 998 319 (中文(简体) ) |
| Ajv - JSON 舍马 | 25 013 168 (简体中文) |
| Arri (编译) - 标准Schema | 15,998,638个 |
| Arri - 标准Schema | 11,765,466人 (单位:人) |
| 维利博特 | 9 831 763美元 (单位:千美元) |
| 佐德语Name | 766 604 (中文(简体) ) |
| 方舟类型 | 439 401 (韩语) |
| 佐德/v4区 | 100,168 (单位:千美元) |
插入解析( 好输入)
| 图书馆 | 行动 |
|---|---|
| Arri (编译) | 136,350,248 (单位:千美元) |
| 亚利 | 49 111 714美元) |
| JSON.parse () (英语) | 21,047,855 (英语) |
INT 解析( 失败输入)
| 图书馆 | 行动 |
|---|---|
| Arri (编译) | 60 790 411美元 (单位:千美元) |
| JSON.parse () (英语) | 12,746,512个 |
| 亚利 | 9 824 069 (中文(简体) ) |
序列化
| 图书馆 | 行动 |
|---|---|
| Arri (编译) - 验证和序列化 | 194 120 420 (中文(简体) ) |
| Arri (编译) | 186 143 375 (中文(简体) ) |
| 蒂皮亚 | 107 523 528美元 |
| 亚利 | 59 302 746 (中文(简体) ) |
| Typia - 验证和序列化 | 47,194,163 (韩语) |
| JSON 强调 | 17 049 686 (单位:千美元) |
强制( 良好输入)
| 图书馆 | 行动 |
|---|---|
| 亚利 | 50,428,605美元 (单位:千美元) |
| 类型框 | 34 878 937 (中文(简体) ) |
| Ajv - JSON 舍马 | 32,125,175 (简体中文) |
| 佐德/v4区 | 14 351 655 (中文(简体) ) |
| 佐德语Name | 1 197 013 (中文(简体) ) |
强迫( 错误输入)
| 图书馆 | 行动 |
|---|---|
| 亚利 | 10 088 606 (简体中文) |
| 类型框 | 8 117 032人次 (单位:千美元) |
| Ajv - JSON 舍马 | 6 979 847 (简体中文) |
| 佐德语Name | 762 348 (韩语) |
| 佐德/v4区 | 99 086 (简体中文) |
发展
大楼
运行 nx build @arrirpc/schema 来建造图书馆.
运行单元测试
运行 nx test @arrirpc/schema 通过 Vitest 执行单位测试
基准制定
运行 nx benchmark @arrirpc/schema 执行基准