首页/新闻资讯/正文详情

在 Convex 中使用 TypeScript exactOptionalPropertyTypes 编写类型安全的 query 与 mutation 函数

发布时间:2026/9/23 20:39:31 来源:云帆数科 栏目:资讯中心
在 Convex 中使用 TypeScript exactOptionalPropertyTypes 编写类型安全的 query 与 mutation 函数
在 Convex 中使用 TypeScript exactOptionalPropertyTypes 编写类型安全的 query 与 mutation 函数【免费下载链接】convex-backendThe open-source reactive database for app developers项目地址: https://gitcode.com/gh_mirrors/co/convex-backend本指南以 convex-backend 仓库中的typescript-exact-optional-property-types演示项目为主体讲解 Convex 服务端函数query / mutation的标准编写方式并深入剖析在开启 TypeScript 严格编译选项exactOptionalPropertyTypes: true后如何正确处理带可选字段v.optional(...)的文档类型。读完本文你将掌握 Convex 函数目录的完整组织方式、参数校验器Validator用法、React 端调用范式以及可选属性类型在数据模型中的源码级实现原理能够直接在自己的 Convex 项目中复刻这套严格类型配置。演示项目定位Convex 函数目录的标准模板与严格类型实验场在 convex-backend 仓库中npm-packages/private-demos/typescript-exact-optional-property-types/ 是一个private demo私有演示项目。与仓库内其他 demo 一样它被用来验证在特定 TypeScript 配置下 Convex 的类型行为。该目录下的 README.md 只有一句话This is a recent TypeScript version with the tsconfig.json optionexactOptionalPropertyTypes: trueset.即这是一个安装了较新 TypeScript 版本、并在 tsconfig.json 中开启了exactOptionalPropertyTypes: true的演示项目用于验证 Convex 的校验器类型推断在该严格选项下是否依然精确。而convex/子目录下的 README.md 则是 Convex 官方为每个项目自动生成的函数目录说明文档它记录了编写 Convex 服务端函数的标准姿势。两者结合正好构成一篇标准用法 严格类型边界的完整技术指南。项目的整体结构如下npm-packages/private-demos/typescript-exact-optional-property-types/ ├── README.md # 项目说明开启 exactOptionalPropertyTypes ├── package.json # 依赖 convexworkspace:*、typescript ^5.9.2 ├── tsconfig.json # 项目级严格 TS 配置 ├── turbo.json └── convex/ # Convex 函数目录 ├── README.md # 官方函数编写指南关联文档 ├── messages.ts # 演示查询函数展示可选属性类型行为 ├── schema.ts # 数据模型含可选字段的表定义 ├── tsconfig.json # 函数运行环境的 TS 配置 └── _generated/ # 自动生成代码api / dataModel / serverConvex 函数目录一切从convex/开始Convex 要求所有服务端函数都放在项目根目录的convex/文件夹下。每一个导出的函数都会自动成为应用公共 API 的一部分客户端通过类型安全的引用如api.myFunctions.myQueryFunction调用它们无需手写 URL 路由或 HTTP 端点。目录中的 convex/tsconfig.json 描述了 Convex 函数的运行环境并用于对这些函数做类型检查。其中有几个由 Convex 强制要求的编译选项不能随意修改{ compilerOptions: { target: ESNext, lib: [ES2023, dom], forceConsistentCasingInFileNames: true, module: ESNext, isolatedModules: true, noEmit: true } }noEmit: trueConvex 函数由 CLI 负责打包与部署本地不做编译产物输出isolatedModules: true保证每个文件可被独立转译适配 Convex 的分文件编译模型module: ESNext与target: ESNext匹配 Convex 运行时的现代 JavaScript 能力。同时这份配置也允许你自行追加严格选项如strict: true在函数侧获得更严格的类型检查。编写第一个 query 函数参数校验器与 handler关联文档给出的 query 函数标准模板如下完整继承自 convex/README.md// convex/myFunctions.ts import { query } from ./_generated/server; import { v } from convex/values; export const myQueryFunction query({ // Validators for arguments. args: { first: v.number(), second: v.string(), }, // Function implementation. handler: async (ctx, args) { // Read the database as many times as you need here. const documents await ctx.db.query(tablename).collect(); // Arguments passed from the client are properties of the args object. console.log(args.first, args.second); // Write arbitrary JavaScript here: filter, aggregate, build derived data, // remove non-public properties, or create new objects. return documents; }, });几个关键点query从./_generated/server导入这不是手写代码而是由npx convex dev自动生成。查看 convex/_generated/server.d.ts 可以看到query被声明为QueryBuilderDataModel, public其类型参数DataModel直接来自你的 schema从而保证ctx.db上的所有表名与文档类型都被静态校验。args用 Validator 描述参数形状v.number()、v.string()来自convex/values。运行时会据此校验入参编译期则据此推断args的 TypeScript 类型。handler是纯异步函数ctx提供数据库读取能力args是经过校验的客户端参数对象。你可以在 handler 中做任意数据变换过滤、聚合、派生数据、剔除非公开字段最后返回给客户端。在 React 组件中调用 queryconst data useQuery(api.myFunctions.myQueryFunction, { first: 10, second: hello, });useQuery接收两个参数函数引用api.xxx与参数对象。由于api来自自动生成的 convex/_generated/api.d.ts其类型为ApiFromModules{ messages: typeof messages }参数对象的字段、类型乃至可选性都会与服务器端args校验器严格对齐——写错参数名或类型会直接在编译期报错。编写 mutation 函数原子写入数据库mutation 与 query 结构相同区别在于它可以写数据库且 Convex 保证一次 mutation 内的所有写入原子生效不会出现半写状态// convex/myFunctions.ts import { mutation } from ./_generated/server; import { v } from convex/values; export const myMutationFunction mutation({ // Validators for arguments. args: { first: v.string(), second: v.string(), }, // Function implementation. handler: async (ctx, args) { // Insert or modify documents in the database here. // Mutations can also read from the database like queries. const message { body: args.first, author: args.second }; const id await ctx.db.insert(messages, message); // Optionally, return a value from your mutation. return await ctx.db.get(messages, id); }, });客户端侧使用useMutation触发const mutation useMutation(api.myFunctions.myMutationFunction); function handleButtonPress() { // fire and forget, the most common way to use mutations mutation({ first: Hello!, second: me }); // OR // use the result once the mutation has completed mutation({ first: Hello!, second: me }).then((result) console.log(result), ); }两种调用方式分别对应无需关心结果fire and forget与等待提交完成并消费返回值两种场景。返回值会经过序列化传输给客户端其类型同样由 Validator 推断。数据模型用v.optional声明可选字段本 demo 的核心实验对象是 convex/schema.ts 中定义的messages表import { defineSchema, defineTable } from convex/server; import { v } from convex/values; export default defineSchema({ messages: defineTable({ author: v.string(), body: v.string(), optionalString: v.optional(v.string()), objectWithOptionalString: v.object({ optionalString: v.optional(v.string()), }), }), });这里展示了两种可选形态顶层可选字段optionalString: v.optional(v.string())文档可以没有该字段嵌套对象中的可选字段objectWithOptionalString一个对象值内部又包含可选的字符串字段。这张表被convex/_generated/dataModel.d.ts中的DataModelFromSchemaDefinitiontypeof schema消费自动推导出messages表的文档类型Docmessages。也就是说schema 是数据模型的唯一事实来源函数里的ctx.db类型全部由此派生。exactOptionalPropertyTypes 到底改变什么先看 TypeScript 层面的语义差异。默认情况下可选属性optionalString?: string的类型被推断为string | undefined并且显式赋值undefined是合法的——此时字段存在但值为 undefined。而开启exactOptionalPropertyTypes: true后可选属性optionalString?: string表示要么该属性完全不存在要么它是一个真正的string把undefined赋给可选属性会被视为类型错误除非属性类型显式写作string | undefined。这正是 tsconfig.json 中注释所强调的该配置参考了 TypeScript 官方 PR 的推荐模板{ compilerOptions: { // Stricter Typechecking Options noUncheckedIndexedAccess: true, exactOptionalPropertyTypes: true, // Recommended Options strict: true, jsx: react-jsx, verbatimModuleSyntax: true, isolatedModules: true, moduleDetection: force, // the convex package doesnt typecheck when using exactOptionalPropertyTypes skipLibCheck: true, noEmit: true, forceConsistentCasingInFileNames: true } }值得注意的配套选项noUncheckedIndexedAccess: true数组索引访问会得到T | undefined这解释了 demo 代码中stuff[0]!的非空断言写法skipLibCheck: true注释明确说明convex 包在 exactOptionalPropertyTypes 下无法完整通过类型检查因此跳过node_modules中.d.ts的检查只对自己的业务代码严格把关verbatimModuleSyntax与moduleDetection: force配合module: nodenext保证 ESM 语义下的导入/导出形式与模块识别正确所以messages.ts中导入使用./_generated/server.js后缀。源码级原理Convex 如何兼容 optional 字段Convex 的v.optional(...)之所以能在exactOptionalPropertyTypes下生成精确类型关键在于 npm-packages/convex/src/values/validator.ts 中ObjectType类型的实现export type ObjectTypeFields extends PropertyValidators Expand // Map each key to the corresponding property validators type making // the optional ones optional. { // This Exclude..., undefined does nothing unless // the tsconfig.json option exactOptionalPropertyTypes: true, // is used. When it is it results in a more accurate type. // When it is not the Exclude removes undefined but it is // added again by the optional property. [Property in OptionalKeysFields]?: Exclude InferFields[Property], undefined ; } { [Property in RequiredKeysFields]: InferFields[Property]; } ;这段源码注释本身就是为exactOptionalPropertyTypes场景专门写的揭示了精妙之处OptionalKeysFields从属性校验器中挑出isOptional optional的键RequiredKeys挑出其余必填键可选键被映射为?: ExcludeInfer..., undefined——先剔除undefined再标记为可选属性在默认未开启exactOptionalPropertyTypes模式下可选属性?: T本身就被推断为T | undefinedExclude剔除undefined后又被可选属性加回来所以结果不变、行为兼容在开启exactOptionalPropertyTypes后?: T不再隐式包含undefined此时预先用Exclude剔除的undefined不会被加回最终类型精确为属性可缺席但一旦存在就是纯string。也就是说同一份 Validator 代码在两种编译选项下都能产出正确的类型这正是该 demo 要验证的兼容性保证。实战验证demo 查询函数中的类型收窄模式convex/messages.ts 中的list查询函数是整套配置的活体测试用例它系统地展示了在exactOptionalPropertyTypes下如何安全消费可选字段import { query } from ./_generated/server.js; export const list query({ args: {}, handler: async (ctx) { const stuff await ctx.db.query(messages).collect(); // (noUncheckedIndexedAccess) const doc stuff[0]!; // exactOptionalPropertyTypes isnt any different when you access this const optionalField: undefined | string doc.optionalString; console.log(optionalField); const { _id, _creationTime, body: _body, author: _author, objectWithOptionalString, ...justOptional } doc; if (optionalString in justOptional) { const exists: string justOptional.optionalString; console.log(exists); } else { const dne: undefined justOptional.optionalString; // ts-expect-error undefined is not assignable to string const exists: string justOptional.optionalString; console.log(dne, exists); } if (optionalString in objectWithOptionalString) { // ts-expect-error building convex with exact-optional-property-types fixes this const exists: string justOptional.optionalString; console.log(exists); } else { // ts-expect-error building convex with exact-optional-property-types fixes this const dne: undefined justOptional.optionalString; // ts-expect-error undefined is not assignable to string const exists: string justOptional.optionalString; console.log(dne, exists); } }, });从中可以提炼出几条可直接复用的模式直接属性访问doc.optionalString的类型是undefined | string注释说明此时exactOptionalPropertyTypes并不会带来差异与默认模式行为一致解构 in操作符收窄将文档解构出justOptional剔除_id、_creationTime及必填字段后再用optionalString in justOptional做运行时存在性检查。存在分支里justOptional.optionalString被收窄为纯string可直接赋给string类型不存在分支里它是undefined赋给string会触发ts-expect-error——这正是exactOptionalPropertyTypes严格语义的直接体现值必须属性存在且为 string或属性缺席二选一嵌套对象场景的已知问题objectWithOptionalString分支中的注释写明building convex with exact-optional-property-types fixes this使用 exactOptionalPropertyTypes 构建 convex 会修复此问题说明 demo 同时记录了嵌套可选对象在类型推断上仍存在的边界情况并用ts-expect-error显式标注保证tsc构建npm run build即tsc在严格配置下依然通过。运行与验证CLI 工作流关联文档最后给出了完整的命令行工作流本 demo 的 package.json 也与之对应# 在项目根目录启动本地开发自动同步函数并生成 _generated 代码 npx convex dev # 查看 Convex CLI 的全部能力 npx convex -h # 启动本地文档服务 npx convex docsnpx convex dev会监听convex/目录将函数推送到本地开发部署并重新生成 convex/_generated/ 下的api.d.ts、dataModel.d.ts、server.d.ts等文件文件头部均注明 To regenerate, runnpx convex dev本 demo 的package.json还提供了npm run dev即convex dev与npm run build即tsc两个脚本后者用于在 CI 或本地验证整个项目含messages.ts中的ts-expect-error断言在exactOptionalPropertyTypes配置下类型检查通过依赖方面项目通过convex: workspace:*直接引用 monorepo 内的 convex 包并搭配typescript: ^5.9.2exactOptionalPropertyTypes自 TypeScript 4.4 起可用5.x 完全支持。小结以 convex/README.md 的标准模板为基础结合typescript-exact-optional-property-types这个演示项目可以总结出一条清晰的实践路径函数即 API在convex/目录用query/mutation包裹 handler用v.*校验器描述参数与文档结构客户端通过生成的api/internal引用获得端到端类型安全schema 驱动类型v.optional(...)声明可选字段后_generated/dataModel.d.ts会推导出精确的文档类型严格选项可选但值得开启在 tsconfig 中追加exactOptionalPropertyTypes: true配合noUncheckedIndexedAccess、strict、skipLibCheck并用in 操作符做存在性收窄即可在属性缺席与属性为 undefined之间获得严格区分兼容性由底层保证validator.ts 中ObjectType的ExcludeInfer..., undefined技巧让同一套 Validator 在两种编译选项下都产出正确类型这正是该 demo 存在并被纳入仓库持续验证的价值所在。如果你想在自己的 Convex 项目中复刻这套严格类型配置直接参考本 demo 的 项目级 tsconfig.json 与 convex/tsconfig.json并把convex/下的 schema 与函数模板迁移过去即可。【免费下载链接】convex-backendThe open-source reactive database for app developers项目地址: https://gitcode.com/gh_mirrors/co/convex-backend创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

K-Means聚类可视化实战:从手写原理到sklearn管线落地
K-Means聚类可视化实战:从手写原理到sklearn管线落地

简介:面向机器学习初学者与工程分析人员,资源以KMeans聚类为切入点,演示如何对故障类型样本进行无监督分组并生成可视化结果。压缩包内共2个文件:一个MATLAB脚本用于执行聚类计算与绘图,一个Excel数据文件提供异常加热… · 2026/9/23 20:39:31

纯Python手写神经网络:从零实现MNIST识别与反向传播
纯Python手写神经网络:从零实现MNIST识别与反向传播

简介:本资源是一份面向Python初学者与机器学习入门者的手写数字识别实践项目,聚焦神经网络算法原理与MNIST数据集实战应用。通过简洁可运行的代码实现全连接神经网络训练与推理,帮助读者理解前向传播、反向传播及权重更新等核心机制&#xff… · 2026/9/23 20:39:31

Flet 用户认证数据模型解析:深入理解 flet.auth.User 与用户资料结构
Flet 用户认证数据模型解析:深入理解 flet.auth.User 与用户资料结构

前端跨平台桌面应用移动开发 【免费下载链接】flet Build realtime web, mobile and desktop apps in Python only. No frontend experience required. 项目地址: https://gitcode.com/gh_mirrors/fl/flet 点击查看 免费下载 导读 flet.auth.User 是 Flet 官方文档… · 2026/9/23 20:39:31

Bruce 固件 Web界面实战指南:如何用浏览器远程操控你的 ESP32 渗透设备
Bruce 固件 Web界面实战指南:如何用浏览器远程操控你的 ESP32 渗透设备

Bruce 固件 Web界面实战指南:如何用浏览器远程操控你的 ESP32 渗透设备 【免费下载链接】firmware Predatory ESP32 Firmware 项目地址: https://gitcode.com/GitHub_Trending/bru/firmware 把设备接上网络,打开浏览器,一块完整的渗透… · 2026/9/23 21:10:05

opencodex 修复 Cursor 工具通道:用 AgentRunRequest.mcp_tools 让注入工具真正可调用
opencodex 修复 Cursor 工具通道:用 AgentRunRequest.mcp_tools 让注入工具真正可调用

【免费下载链接】opencodex Universal provider proxy for OpenAI Codex & Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code 项目地址: https://gitcode.com/gh_mirrors/ope/opencodex 点击… · 2026/9/23 21:09:59

XP关机变重启?从ACPI和BIOS排查断电与唤醒问题
XP关机变重启?从ACPI和BIOS排查断电与唤醒问题

简介:电脑XP系统关机异常,表现为无法正常关机或关机后自动重启,是一类常见且令人困扰的故障。这份小型PDF资料面向使用Windows XP的老用户、电脑维护人员和网络管理员,系统梳理了造成该问题的典型原因,包括退出声音文件… · 2026/9/23 21:09:52

Windows蓝牙音质提升指南:用Alternative A2DP Driver解锁LDAC与aptX HD
Windows蓝牙音质提升指南:用Alternative A2DP Driver解锁LDAC与aptX HD

1. 为什么要在Windows上折腾LDAC这件事先说结论:Windows系统自带的蓝牙音频栈,对高音质编解码器的支持一直是个短板。你花大几百甚至上千块买的支持LDAC的耳机,插到Windows电脑上,大概率只能跑SBC或者AAC,音质直接打回… · 2026/9/23 21:09:52

Semver 语义化版本速查指南:版本号、范围表达式与 npm 工程实践
Semver 语义化版本速查指南:版本号、范围表达式与 npm 工程实践

Semver 语义化版本速查指南:版本号、范围表达式与 npm 工程实践 【免费下载链接】reference 为开发人员分享快速参考备忘清单(速查表) 项目地址: https://gitcode.com/jaywcjlove/reference Semantic Versioning(语义化版本,简称 Semv… · 2026/9/23 21:09:52

网络运维述职报告怎么写:数据准备与五段式结构全解析
网络运维述职报告怎么写:数据准备与五段式结构全解析

简介:网络运维部优秀述职报告范文.docx 是一份可直接编辑套用的 Word 述职报告模板,适合网络运维工程师、部门主管及行政人事人员参考,用于快速撰写结构完整、数据量化的年度或半年度述职材料。文档以真实岗位职责为蓝本,围绕交换… · 2026/9/23 21:09:52

3招搞定手机怎么下载微信面试难题实战项目解析
3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧
Win7无线热点配置工具源码解析:解决API失效的3个实战技巧

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧 Win7无线热点配置工具在Win10/11上跑不动?不是你的问题,是版本升级后 API 全变了。很多老项目里的 netsh wlan… · 2026/9/23 0:00:36

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码