运行时类型:无需 schema 的验证与序列化
类型编译器使你的 TypeScript 代码中的类型在运行时可用,包括接口、泛型、联合类型以及 Omit 等映射类型。使用你已有的类型进行验证、类型守卫、类型转换和序列化,而无需在旁边另写 Zod schema 或 class-validator 装饰器。
了解更多你是 LLM 吗?跳过这些像素吧,我们专门为你写了 markdown: llms.txt (llms-full.txt)
类型即是验证器、JSON 格式、数据库 schema 和 API 文档。无需同步维护 Zod schema、class-validator DTO 或 Prisma 模型。
每个 HTTP 请求、RPC 调用、数据库行、配置值和 CLI 参数都会在运行时根据其类型进行检查,并给出出错位置的精确路径。
验证器、序列化器和数据库映射器会针对你的类型编译为普通的 JavaScript 函数。严格检查,却无需付出性能代价。
没有 schema DSL,没有用于依赖注入的装饰器,没有代码生成。就是你已经在写的 TypeScript,也是语言模型最擅长编写的 TypeScript。
下面的 User 类同时是验证规则、JSON 格式、数据库表和 API 文档。HTTP 请求体会在你的代码运行之前被验证和转换,无效输入将返回 400 以及精确的错误信息。
import { App } from '@deepkit/app'; import { FrameworkModule } from '@deepkit/framework'; import { HttpBody, HttpQuery, HttpRouterRegistry } from '@deepkit/http'; import { Database } from '@deepkit/orm'; import { SQLiteDatabaseAdapter } from '@deepkit/sqlite'; import { AutoIncrement, Email, Maximum, MinLength, PrimaryKey, Unique } from '@deepkit/type'; // validation, serialization, database table and API docs in one type class User { id: number & PrimaryKey & AutoIncrement = 0; created: Date = new Date(); username: string & MinLength<3> & Unique = ''; email: Email = ''; } class AppDatabase extends Database { constructor() { super(new SQLiteDatabaseAdapter('app.sqlite'), [User]); } } const app = new App({ providers: [AppDatabase], imports: [new FrameworkModule({ migrateOnStartup: true })], }); const router = app.get(HttpRouterRegistry); // POST /users {"username": "peter", "email": "[email protected]"} router.post('/users', async (user: HttpBody<User>, database: AppDatabase) => { await database.persist(user); return user; }); // GET /users?limit=10 router.get('/users', (database: AppDatabase, limit: HttpQuery<number> & Maximum<100> = 10) => { return database.query(User).limit(limit).find(); }); app.run();
$ curl -X POST localhost:8080/users -d '{"username": "pe", "email": "nope"}' HTTP/1.1 400 Bad Request { "message": "Validation error:\nusername(minLength): Min length is 3 caused by value \"pe\",\nemail(pattern): Pattern ^\\S+@\\S+$ does not match caused by value \"nope\"", "errors": [ { "path": "username", "code": "minLength", "message": "Min length is 3", "value": "pe" }, { "path": "email", "code": "pattern", "message": "Pattern ^\\S+@\\S+$ does not match", "value": "nope" } ] }
运行时类型使 Deepkit 在面对智能体编写的代码时具有独特优势。应用程序的每个边界都会在运行时根据 TypeScript 类型进行验证和序列化,因此生成的代码会在错误代价高昂的地方受到检查。
错误的字段名,或在应为数字的位置出现字符串,都会在边界处被拒绝并给出精确路径,而不是被存入数据库、数周后才被发现。
在你的类型之外不存在第二种 schema 语言,模型也就不会让它们产生细微的不同步。只要 TypeScript 正确,验证、数据库和 API 就都正确。
HTTP、RPC、ORM、依赖注入、配置和 CLI 都来自同一个框架,而不是拼凑在一起的五个库,因此生成的代码在整个应用程序中保持一致。
Deepkit 在运行时将针对你的类型的验证器、序列化器和数据库映射器编译为普通的 JavaScript 函数。每次操作耗时,越低越好,与最常用的库进行对比测量。
类型编译器使你的 TypeScript 代码中的类型在运行时可用,包括接口、泛型、联合类型以及 Omit 等映射类型。使用你已有的类型进行验证、类型守卫、类型转换和序列化,而无需在旁边另写 Zod schema 或 class-validator 装饰器。
了解更多interface Order { id: number & Positive; items: { sku: string & MinLength<3>; quantity: number }[]; shipped?: Date; } if (is<Order>(data)) { data.items; // type guard, data is an Order here } validate<Order>(data); // [{ path: 'items.0.sku', code: 'minLength', ... }] cast<Order>({ id: '1', items: [], shipped: '2026-10-31' }); // { id: 1, items: [], shipped: Date } typeOf<Order>(); // all type information at runtime
// GET /users/34 router.get('/users/:id', (id: number & Positive, db: AppDatabase) => db.query(User).filter({ id }).findOne(), ); // or as controller class class UserController { constructor(private db: AppDatabase) {} // GET /users?limit=10 @http.GET('/users') list(limit: HttpQuery<number> & Maximum<100> = 10) { return this.db.query(User).limit(limit).find(); } }
构造函数参数根据其类型解析,包括接口。无需 @Injectable(),无需注入令牌,无需 reflect-metadata。模块、作用域和配置都属于同一个容器,该容器会被编译为普通的 JavaScript。
了解更多interface Mailer { send(to: string, text: string): Promise<void>; } class SmtpMailer implements Mailer { async send(to: string, text: string) { /* ... */ } } class Signup { // resolved by its type, an interface constructor(private mailer: Mailer) {} async register(email: string) { await this.mailer.send(email, 'Welcome!'); } } const app = new App({ providers: [Signup, provide<Mailer>(SmtpMailer)], });
实体是带有类型注解的类或接口。表、索引和迁移均由类型推导而来。提供工作单元、标识映射、关系以及返回真实类实例的类型安全查询 API。
了解更多class User { id: number & PrimaryKey & AutoIncrement = 0; created: Date = new Date(); constructor( public username: string & MinLength<3> & Unique, public active: boolean = true, ) {} } const database = new Database( new PostgresDatabaseAdapter({ database: 'app' }), [User], ); await database.migrate(); await database.persist(new User('peter')); const users = await database.query(User) .filter({ active: true, created: { $gt: lastWeek } }) .orderBy('created', 'desc') .limit(10) .find(); // User[], real instances
通过 WebSocket 或 TCP,像调用本地函数一样调用服务器控制器的方法。参数和结果会根据其类型进行验证和序列化,包括类、日期以及用于流的 RxJS Observable。
了解更多// server @rpc.controller('/users') class UserController { constructor(private db: AppDatabase) {} @rpc.action() async find(username: string): Promise<User | undefined> { return await this.db.query(User) .filter({ username }) .findOneOrUndefined(); } } // client, in the browser or another server const client = new RpcWebSocketClient('ws://localhost:8080'); const users = client.controller<UserController>('/users'); const user = await users.find('peter'); // User | undefined
class AppConfig { databaseUrl: string = 'postgres://localhost/app'; pageSize: number & Positive = 20; } class UserList { // only the part of the configuration it needs constructor(private pageSize: AppConfig['pageSize']) {} } const app = new App({ config: AppConfig, providers: [UserList], }); // $ app user:create peter --admin app.command('user:create', ( username: string & MinLength<3>, admin: boolean & Flag = false, ) => { // ... }); // APP_PAGE_SIZE=50 sets pageSize, -1 fails at startup app.loadConfigFromEnv().run();
Deepkit 的每个部分都是独立的包。你可以使用整个框架,也可以只使用你需要的库。
从 App 开始构建 CLI 工具或服务,再添加 Framework 获得完整的服务器。每个库都以模块的形式接入,并拥有各自的配置和服务,因此项目可以从单个文件成长为模块化应用,而无需改变其基础。
你的服务之间以及与客户端之间的通信。消息采用二进制编码,并在两端根据其类型进行验证。Broker 为应用的所有实例提供共享的队列、缓存和锁:开发时在内存中运行,生产环境中运行在 broker 服务器或 Redis 上,无需修改代码。
一套 API 处理文件,无论它们存储在哪里。在本地磁盘上开发,在内存中测试,在生产环境中运行于 S3 或 Google Cloud Storage,只需在配置中切换适配器。读写文件的代码保持不变。
你的实体是 TypeScript 类或接口。相同的模型和相同的查询适用于 PostgreSQL、MySQL、SQLite 和 MongoDB,因此你可以在 SQLite 上测试,在 PostgreSQL 上部署,或日后更换数据库,而无需重写数据层。
构成 Deepkit 的基础组件。每个组件都可以在任何 TypeScript 项目中独立使用,无论是后端、前端还是库;当你使用更多组件时,它们也能完美配合。
基于你已编写的类型构建的工具:API 文档、数据库浏览器和调试器均来自你的代码,而不是需要你持续更新的独立定义。
其他库所依赖的底层库,当你恰好需要某个部分时也可单独使用。
运行时类型需要在构建中使用类型编译器。它可与 TypeScript 编译器、Vite 和 Bun 配合使用,因此你可以继续使用现有的工具链。