背景
源自 #2023 的排查:示例应用从根 @objectstack/spec 导入类型 → 解析成 any → 静默吞掉所有对象字面量的类型检查,掩盖了约 30 个真实类型错误。根因之一是这些域没有一个统一、类型安全的编写入口 ,作者(尤其是 AI)只能写裸 : X 字面量,而输出态(z.infer)会把 .default() 字段变必填、拒绝 CEL 字符串简写。
后续的 #2026 / #2029 已经堵上了 CI 闸门(示例 typecheck + ESLint 导入守卫)。本 issue 处理更上游的一致性问题。
问题:三种并存的编写惯用法
写法
适用域
输入态人体工学
编写处类型安全
运行期校验
defineX() 工厂
view/flow/job/agent/app/portal/dataset/book… (19 个)
✅
✅
✅ (.parse())
ObjectSchema.create() / Field.x() 构建器
object / field
✅
✅
✅
裸类型字面量 : X
下列 16 个域
❌
⚠️
❌
用哪种纯粹看历史,无规律。第三类正是 #2023 翻车的地方。
缺口清单(当前 main 实测)
16 个可编写域没有 defineX 工厂:
Datasource、Connector、Policy、SharingRule、Role、PermissionSet、EmailTemplateDefinition、Report、Webhook、ObjectExtension、Cube、Mapping、Theme、TranslationBundle、Page、Action
其中 6 个连 XInput 别名也没有 (需一并补):Policy、Cube、Mapping、Theme、TranslationBundle、Page
(已有 defineX 的 19 个域作为模板:defineView/defineForm/defineApp/defineFlow/defineJob/defineBook/defineAgent/defineTool/defineSkill/definePortal/defineDataset/defineStack/defineSeed/defineSolutionBlueprint/defineViewItem/defineActionDescriptor/defineFlowBuilderConfig/defineObjectDesignerConfig/defineStudioPlugin)
长期价值
让 fix(examples): typecheck example apps clean + gate in CI #2023 那一类 bug 在结构上不可能再发生 :defineX 是值 导入,坏掉会立刻硬报错,没有 any 可退化躲藏。
AI 只需学一种写法 (对齐"模板都是 AI 写的、避免 AI 犯错"的北极星):消灭"看着对其实错"的输出态字面量选项。
编写期错误信息好得多 :.parse() 接已导出的错误映射(objectStackErrorMap / safeParsePretty),给出 webhook.timeoutMs 必须 ≥ 1000 这类就地、字段级提示,而非 TS 结构不匹配的墙。
隔离 schema 变动 :给某 schema 加 .default() 不再静默打破生态里所有裸输出态字面量。
defineX(config: z.input<typeof XSchema>): X { return XSchema.parse(config); } 是唯一同时满足"输入态人体工学 + 编写处类型安全 + 运行期校验"三项的写法;裸 : XInput 只满足前两项。
建议落地方式(两个 PR)
成本 / 取舍
公共 API 面增长(纯增量、零设计风险,照搬现有写法)。
流入 defineStack 的域会双重 parse —— 开销可忽略,换来更早/就地校验。
不动 object/field 的构建器路径(它们已有良好入口)。
TranslationBundle 是 z.record,defineTranslationBundle 形态略特殊但仍成立。
验收标准
🤖 由本次 #2023 /#2026 /#2029 排查衍生,Claude Code 整理
背景
源自 #2023 的排查:示例应用从根
@objectstack/spec导入类型 → 解析成any→ 静默吞掉所有对象字面量的类型检查,掩盖了约 30 个真实类型错误。根因之一是这些域没有一个统一、类型安全的编写入口,作者(尤其是 AI)只能写裸: X字面量,而输出态(z.infer)会把.default()字段变必填、拒绝 CEL 字符串简写。后续的 #2026 / #2029 已经堵上了 CI 闸门(示例 typecheck + ESLint 导入守卫)。本 issue 处理更上游的一致性问题。
问题:三种并存的编写惯用法
defineX()工厂.parse())ObjectSchema.create()/Field.x()构建器: X用哪种纯粹看历史,无规律。第三类正是 #2023 翻车的地方。
缺口清单(当前 main 实测)
16 个可编写域没有
defineX工厂:Datasource、Connector、Policy、SharingRule、Role、PermissionSet、EmailTemplateDefinition、Report、Webhook、ObjectExtension、Cube、Mapping、Theme、TranslationBundle、Page、Action其中 6 个连
XInput别名也没有(需一并补):Policy、Cube、Mapping、Theme、TranslationBundle、Page(已有
defineX的 19 个域作为模板:defineView/defineForm/defineApp/defineFlow/defineJob/defineBook/defineAgent/defineTool/defineSkill/definePortal/defineDataset/defineStack/defineSeed/defineSolutionBlueprint/defineViewItem/defineActionDescriptor/defineFlowBuilderConfig/defineObjectDesignerConfig/defineStudioPlugin)长期价值
defineX是值导入,坏掉会立刻硬报错,没有any可退化躲藏。.parse()接已导出的错误映射(objectStackErrorMap/safeParsePretty),给出webhook.timeoutMs 必须 ≥ 1000这类就地、字段级提示,而非 TS 结构不匹配的墙。.default()不再静默打破生态里所有裸输出态字面量。defineX(config: z.input<typeof XSchema>): X { return XSchema.parse(config); }是唯一同时满足"输入态人体工学 + 编写处类型安全 + 运行期校验"三项的写法;裸: XInput只满足前两项。建议落地方式(两个 PR)
defineX工厂(每个约 3 行,与现有 19 个机械同构),并补齐 6 个缺失的XInput别名。根index.ts按现有约定 re-export。带minorchangeset(@objectstack/spec)。成本 / 取舍
defineStack的域会双重 parse —— 开销可忽略,换来更早/就地校验。TranslationBundle是z.record,defineTranslationBundle形态略特殊但仍成立。验收标准
defineX工厂并从根 re-exportXInput别名补齐pnpm --filter './examples/*' typecheck与pnpm lint全绿@objectstack/specchangeset 已添加🤖 由本次 #2023/#2026/#2029 排查衍生,Claude Code 整理