关于 type-definition-creator
type-definition-creator 用于编写 Datex Studio 自定义接口和枚举,帮助开发者选择可复用或内联结构、定义字段并验证平台配置变更。
主要功能
- 01创建接口和枚举定义
- 02建模内联嵌套对象
- 03引用其他自定义类型
- 04验证并更新类型配置
适用场景
- 01为组件参数定义共享结构
- 02创建固定的命名值集合
- 03表示单个父类型使用的嵌套对象
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ datex/ skills --skill type-definition-creator安装后在 Claude Code 中输入/type-definition-creator确认已加载。
附带文件evals/evals.jsonreferences/type-definitions.md
详细介绍
类型定义创建器
在分支上编写或修改 Datex Studio 类型定义(configurationTypeId=22)——即声明接口(i_<name>)或枚举(e_<name>)的 -customType.json 文件。两种结构属于同一种组件类型,且使用相同后缀,但内部结构差异很大。类型定义会在整个平台中使用:包括 flow inParams / outParams、数据源 objectTypeDef、其他类型定义,以及(有一项重要限制的)UI 组件参数。
参考资料
- ../datex-studio-shared/branch-setup.md — 分支/连接选择(各技能通用)
- ../datex-studio-shared/context-navigation.md#discovering-custom-types-and-enum-members —
dxs configuration nomenclature -b <id>(可选--package/--kind/--search):快速、扁平地列出分支上的所有<Package>.<Type>(以及每个枚举的成员)——编写前运行此命令,检查类型/枚举是否已存在,并确认准确的referenceName和成员名称,无需获取完整内容 - references/type-definitions.md — 权威的类型定义参考:文件结构、接口的六种属性描述符结构、枚举的两种值描述符结构、禁止自引用及内联到指定深度的变通方案、UI 组件枚举 FQN 限制及基本类型加类型转换的变通方案、收紧规则会波及消费者的影响规则
- ../datex-studio-conventions/file-format.md —
configurationTypeId表;JSON 文件位置 - ../datex-studio-conventions/naming-conventions.md —
i_*/e_*前缀规则;-customType.json后缀;文件名主体匹配规则 - ../datex-studio-conventions/defaults.md —
accessModifier默认值(接口和枚举都必须显式选择——从头编写时请询问用户)
依赖项
requirements-gathering技能 — 如果当前对话上下文中尚无需求概要,则调用此技能生成概要impact-analysis技能 — 修改现有且已被使用的类型时,任何收紧变更之前都要调用此技能:添加必填字段、移除可选字段、将属性从通用object收窄为特定结构、将objectTypeDef从null改写为严格定义、重命名属性,或完全移除该类型。该类型的 FQN(<Package>.<referenceName>)是追踪目标;所有引用了宽松结构、并访问新结构未声明属性的消费者,都会在平台的严格检查中出错。全新类型无需执行此步骤
CLI 生命周期
类型定义编写通过 dxs configuration 完成——这是适用于平台所有配置类型的通用 CRUD 基元。没有 dxs customtype 子命令,也不支持字段级修补;需要构建(或获取并提取)整个 JSON 正文,编辑后再整体推送回去。CLI 中的类型标识符是 customtype(小写,与 ConfigurationEndpoints.normalize_type 的输出一致),对应 configurationTypeId: 22。接口(i_<name>)和枚举(e_<name>)共用此组件类型——由内部的 type 字段("interface" 与 "enum")加以区分。
创建新的类型定义:
# 1. Build body.json from scratch (see references/type-definitions.md → Interfaces / Enums)
# 2. Validate — gates the push. Exit 1 = errors found (read validation_errors, fix, re-run), not a broken CLI
dxs configuration validate customtype -b <branchId> -D body.json
# 3. Create
dxs configuration upsert customtype -b <branchId> -D body.json
编辑现有类型定义:
# 1. Fetch — note the envelope wrapper
dxs configuration get customtype <configId> -b <branchId> -O envelope.json
# 2. EXTRACT THE INNER BODY (round-trip footgun guard — see "Round-trip rule" below)
jq .json envelope.json > body.json
# 3. Edit body.json
# 4. Validate — gates the push. Exit 1 = errors found (read validation_errors, fix, re-run), not a broken CLI
dxs configuration validate customtype -b <branchId> -D body.json
# 5. Push
dxs configuration upsert customtype -b <branchId> -D body.json
往返规则(关键)
编辑现有配置时,绝不能将 envelope.json 直接传入 dxs configuration upsert——这会悄无声息地破坏配置内容。上述修正后的流程(先用 jq 提取内部的 .json,再进行编辑)是所有往返操作的必要步骤。有关标准往返流程和底层错误,请参阅 ../datex-studio-shared/configuration-roundtrip.md。
与其他配置类型相比,类型定义的主体异常精简——没有 inParams / outParams / vars / events / flows 内容槽位(这些都保持为 null)。主体本质上是组件身份信封,加上 objectTypeDef[](接口)或 enumTypeDef[](枚举)之一。往返规范(获取 → 用 jq 提取 → 编辑 → 验证 → 推送)仍然适用——无论主体大小如何,平台的导入路径都会执行完整的信封感知更新。类型定义文件在磁盘上也采用单行压缩 JSON 格式(与其他组件文件不同,请勿美化格式)。
工作流
[Phase 1: Setup + Requirements]
Follow branch-setup.md for branch/connection selection
|
[requirements brief in context?]
+-----+-----+
| |
YES NO -> invoke `requirements-gathering`
| |
+-----+------+
|
[Phase 2: Decide interface vs enum vs nested]
Consult references/type-definitions.md:
- shared shape that multiple components reference
-> interface (i_<name>) — objectTypeDef[] with property
descriptors; type:"interface"; enumTypeDef:null;
isStringValue:null
- fixed set of named values (string-valued kebab-case or
number-valued stringified integer)
-> enum (e_<name>) — enumTypeDef[] with value descriptors;
type:"enum"; objectTypeDef:[] (empty array, NOT null);
isStringValue:true (string) OR null (number)
- anonymous nested structure used only inside one parent
type, no separate FQN
-> nested inline object on the parent's objectTypeDef[]
(no separate -customType.json file)
|
[Phase 3: Author type body]
Build body.json (single-line minified):
- File basics: suffix -customType.json, configurationTypeId 22,
referenceName and title both equal filename stem, description
<= 100 chars, accessModifier set (confirmed with user)
- Interface body: objectTypeDef[] array of property descriptors,
each carrying id / type / isCollection / required / objectType /
isSecured; the six property shapes (scalar, array of primitives,
inline object, FQN reference, union, object collection)
- Enum body: enumTypeDef[] array of value descriptors; ProperCase
ids, kebab-case values by default for string-valued; number-valued
entries carry description:null
- References use FQN ("<Package>.<referenceName>") — never the
referenceName alone
- Self-reference prohibition: custom types cannot reference
themselves by FQN — inline to a fixed maximum depth matching
the producing engine's recursion cap; maintain via a generator
script alongside the file
- UI-component enum FQN constraint: if this type (or an enum from
this package) will be used inside vars/inParams/outParams of an
editor/form/selector/hub/grid, do NOT declare it as
objectType:"<Package>.e_<enum>" in those param descriptors —
declare as the underlying primitive and cast at use time;
flow-tier components and other custom-type files can reference
enums via FQN freely
- Sibling slots stay null: inParams, outParams, vars, events all
null — type definitions have no code strings of their own
|
[Phase 4: Validate + push]
dxs configuration validate customtype -b <branchId> -D body.json
|
+----+----+
| |
CREATE MODIFY-EXISTING
| |
| use the corrected round-trip
| (get -O envelope -> jq .json -> body)
| |
+----+----+
|
v
dxs configuration upsert customtype -b <branchId> -D body.json
(upsert creates or updates by referenceName — one command for both)
|
[Phase 5: (Optional) Impact analysis if tightening or removing]
For tightening edits on an existing type (adding required fields,
removing optionals, narrowing object -> specific shape, rewriting
objectTypeDef null -> strict, renames, removal), invoke the
`impact-analysis` skill with target <Package>.<referenceName>
to enumerate every reference site and audit each one against the
new shape. Net-new types skip this step.
|
[invoke `post-edit-verification`; then `component-validator`]
阶段详情
阶段 1:设置 + 需求
- 按照 ../datex-studio-shared/branch-setup.md 选择分支和连接。切勿假设分支 ID——请向用户确认。
- 检查对话上下文中是否已有需求简述(由
requirements-gathering或其他调用技能生成)。- 已有简述——使用它。简述应明确该类型的用途(多个组件间共享的数据结构、固定值集合,或某个父类型中的匿名嵌套结构)、会引用它的使用方(流程参数、数据源结构、UI 组件),以及
accessModifier应为public还是private。 - 没有简述——先调用
requirements-gathering技能。事先正确决定使用接口、枚举还是嵌套结构,可避免使用方接入后再重构类型。
- 已有简述——使用它。简述应明确该类型的用途(多个组件间共享的数据结构、固定值集合,或某个父类型中的匿名嵌套结构)、会引用它的使用方(流程参数、数据源结构、UI 组件),以及
阶段 2:决定使用接口、枚举还是嵌套结构
编写前请参阅 references/type-definitions.md。此决定会影响主体结构:
接口(i_<name>)——供多个组件引用的共享结构。type: "interface",主体为包含属性描述符的 objectTypeDef[]。当相同结构出现在两个或更多位置(某个流程的 inParams、某个数据源的 outParams[].objectTypeDef、另一个接口的嵌套引用)时,集中定义可避免结构不一致。
枚举(e_<name>)——一组固定的命名值。type: "enum",主体为包含值描述符的 enumTypeDef[]。字符串枚举使用 isStringValue: true(默认采用 kebab-case 格式的 value);数字枚举使用 isStringValue: null(不能是 false),每个条目的 value 为字符串形式的整数,且 description: null。当调用方需要一组可通过引用常量识别的固定值时使用。
匿名嵌套内联对象——当结构只存在于某个父类型内部且没有独立身份时,将其内联为父类型 objectTypeDef[] 中 type: "object" 的属性描述符,并包含由子描述符构成的嵌套 objectTypeDef[]。无需单独的 -customType.json 文件。当结构不共享,且为其命名并无实际收益时使用。
如有疑问,请问:“是否会有多个组件按名称引用这个完全相同的结构?”如果是 → 接口。“这个结构是否只是一组固定选项?”如果是 → 枚举。否则 → 内联嵌套结构。请参阅 references/type-definitions.md → 接口、references/type-definitions.md → 枚举。
阶段 3:编写类型主体
根据 references/type-definitions.md → Interfaces 和 → Enums 中的骨架构建 body.json(单行压缩 JSON——不要美化输出)。要点:
-
文件基础信息。 关于
-customType.json文件的格式,请遵循下方的 预检清单以及 ../datex-studio-conventions/universal-checklist.md。构建时的类型特定要求:accessModifier为"public"或"private"——从零开始创建时,请向用户确认选择(通用清单默认使用public;自定义类型通常需要private)。 -
接口主体。 顶层常量:
type: "interface"、baseTypes: null(暂不支持继承)、enumTypeDef: null、isStringValue: null、inParams: null、outParams: null、vars: null、events: null。主体为objectTypeDef[]——一个属性描述符数组。每个属性(包括顶层属性和内联对象中的嵌套属性)都包含id/type/isCollection/required/objectType/isSecured: false。六种属性形状(标量基本类型、基本类型数组、内联对象、对另一个接口或枚举的 FQN 引用、带有oneOf[]的联合类型、对象集合)详见 references/type-definitions.md → Interfaces。 -
枚举主体。 顶层常量:
type: "enum"、objectTypeDef: [](空数组,不是null)、baseTypes: null、inParams: null、outParams: null、vars: null、events: null。每个文件的设置:字符串值枚举使用isStringValue: true;数字值枚举使用isStringValue: null(不是false)。enumTypeDef[]——一个值描述符数组。值描述符命名规则:id(引用键)使用 PascalCase,不带空格(例如RoyalBlue、ForestGreen);字符串值枚举的value默认使用 kebab-case(例如royal-blue、forest-green),除非用户另有指定。数字值条目的整数以字符串形式存储(例如"value":"0"),且description: null。 -
引用使用 FQN。
objectType的格式为"<Package>.<referenceName>"——不能只有 referenceName。接口引用("Allocations.i_preallocation_action")和枚举引用("Allocations.e_allocation_replenishment_context")都遵循此格式。在接口中,枚举引用采用与接口引用相同的{type:"object", objectType:"<Package>.<enum_referenceName>", ...}形状(枚举的运行时值为字符串或数字类型,但架构层面的引用通过type:"object"实现)。 -
不允许自引用。 自定义类型不能递归引用自身——不存在
objectType: "<Package>.<this_type>"这样的递归路径。如果类型需要包含自身的嵌套子项(例如具有任意深度children的树),请将结构内联到固定的最大深度——在每个层级重复完整的属性描述符集。如果生成树的引擎具有硬性递归上限,请让类型的内联深度与该上限一致,以确保类型能容纳引擎可生成的所有值。请使用与文件一同维护的生成器脚本来维护内联副本;只手动编辑某一层而不更新其他层属于缺陷。请参阅 references/type-definitions.md → Custom Types Cannot Self-Reference。 -
UI 组件的枚举 FQN 限制。 在 UI 组件(编辑器、表单、选择器、Hub、网格)的
vars、inParams和outParams中,属性描述符不能使用objectType: "<Package>.e_<enum>"引用。平台对 UI 组件参数声明进行 TS 验证时,不会解析自定义$types.*命名空间,并会产生Cannot find namespace '_types'. Did you mean 'Type'?(_types是平台将$types重写为_types后留下的产物)。解决方法:将字段声明为底层原始类型(字符串值枚举使用string,数值枚举使用number),然后在流程代码的使用处进行类型转换(const v = $form.fields.x.control.value as $types.<Package>.e_<enum>;)。此限制仅适用于 UI 组件——流程层组件(操作、函数、流程数据源)和自定义类型文件均可自由声明objectType: "<Package>.e_<enum>"引用。请参阅 references/type-definitions.md → UI Components Cannot Reference Custom Enums in vars / inParams / outParams。 -
同级槽位保持为 null。 类型定义主体中的
inParams、outParams、vars、events始终为null——类型定义自身没有代码字符串。不要臆造代码槽位;请遵循惯例。
第 4 阶段:验证并推送
# Validate the body locally against the branch. Exit 1 = validation found errors
# (read validation_errors, fix body.json, re-run) — not a broken CLI. Do not push on exit 1.
dxs configuration validate customtype -b <branchId> -D body.json
# For a new type definition
dxs configuration upsert customtype -b <branchId> -D body.json
# For modify-existing (round-trip — never skip the jq extract)
dxs configuration get customtype <configId> -b <branchId> -O envelope.json
jq .json envelope.json > body.json
# ... edit body.json ...
dxs configuration upsert customtype -b <branchId> -D body.json
验证会在推送前发现缺少必填字段、属性描述符或值描述符结构不正确,以及结构错误。它不会捕获自引用限制(该问题会在使用方编译时表现为类型加载器失败)、UI 组件的枚举 FQN 限制(该问题会在使用方 UI 组件上表现为 Cannot find namespace '_types'),或收紧类型导致影响使用方的问题(该问题会在每个此前隐式获得 any 的引用位置表现为严格检查类型错误)。推送前,请阅读 references/type-definitions.md 中介绍这些问题的各个部分。
第 5 阶段:(可选)收紧或移除时进行影响分析
对于全新类型,跳过此阶段——目前还没有任何内容引用该类型。
对于已在使用中的现有类型,以下任一项都属于会影响使用方的收紧编辑:
- 添加之前的补丁中未包含的必填字段。
- 移除使用方可能仍在读取的可选字段。
- 将属性从通用的
type: "object"收窄为特定的 FQN 引用或严格的内联结构。 - 将
objectTypeDef从null(开放结构)改写为严格的描述符数组。 - 重命名属性——所有访问旧名称的使用方都会出错。
- 完全移除该类型——所有 FQN 引用位置都会出错。
对于以上任一情况,请在推送前调用 impact-analysis 技能,目标设为 <Package>.<referenceName>(该类型的 FQN)。该技能会枚举整个代码库中的所有引用位置——流程 inParams / outParams、数据源 objectTypeDef、其他类型定义,以及使用 objectType 引用的 UI 组件参数——以便在同一次编辑中根据新结构审查每个位置。此前隐式获得 any 并读取新类型未声明字段的代码,会在平台的严格检查阶段出错。不要在父 Agent 中内联 grep 调用方——多文件扫描是专用技能的工作。请参阅 references/type-definitions.md → Tightening a Previously-Loose Type Ripples to Consumers。
推送前检查清单
推送前,请完整阅读 references/type-definitions.md 中的内容。快速版:
- 文件基础信息。 后缀为
-customType.json,configurationTypeId: 22;新文件的accessModifier已与用户确认——另请完成通用检查(../datex-studio-conventions/universal-checklist.md)。 - 类型区分正确。 接口使用
type: "interface"、enumTypeDef: null、isStringValue: null、objectTypeDef: [...]。枚举使用type: "enum"、objectTypeDef: [](空数组,不是null)、enumTypeDef: [...]、isStringValue: true或null(绝不能是false)。 - 属性描述符完整。
objectTypeDef中的每个条目(顶层和嵌套)都有id、type、isCollection、required、objectType、isSecured。内联对象包含objectTypeDef;FQN 引用包含objectType;联合类型包含oneOf[]。 - 枚举的
id使用 ProperCase,且不含空格。value遵循指定的大小写格式(字符串值默认使用 kebab-case)。数值条目使用字符串形式的整数,并设置description: null。 - 引用使用 FQN。
objectType格式为"<Package>.<referenceName>",而不只是 referenceName。 id占位值。 编写时使用id: 0;实际值由用户或导入过程分配。- 不得自引用。 如果类型需要嵌套自身的子项,则将其内联到与生成引擎递归上限相符的固定最大深度;通过生成器脚本维护。
- 遵循 UI 组件枚举 FQN 限制。 如果此类型(或此软件包中的枚举)将用于编辑器、表单、选择器、hub 或网格的
vars、inParams或outParams中,请勿在这些声明中写入objectType: "<Package>.e_<enum>"——应声明为底层基元类型,并在使用时转换。 - 同级字段设为 null。
inParams、outParams、vars、events均保持为null——类型定义本身没有代码字符串。 - 收紧类型的修改须先进行影响分析。 对正在使用的类型进行任何会添加必填字段、移除可选字段、缩窄属性范围、将
objectTypeDef从null改为严格定义、重命名属性或移除类型的修改,都必须在推送前针对该类型的 FQN 使用impact-analysis技能。新增类型无需执行此步骤。
常见错误
| 错误 | 修正 |
|---|---|
数值枚举的 isStringValue 被设为 false 而不是 null | 平台对 false 和 null 的处理不同——数值枚举必须使用 null(且条目带有 description: null 和字符串化整数 value)。请准确设置为 isStringValue: null。 |
枚举的 objectTypeDef 被设为 null 而不是 [] | 枚举使用 objectTypeDef: [](空数组)——null 是接口的约定。平台的加载器通过此字段区分这两种结构;设为 null 会导致枚举结构错误。 |
枚举的 id 含有空格或不符合 ProperCase 的字符 | id 中不允许有空格。请使用 ProperCase(例如 RoyalBlue、ForestGreen)。 |
属性描述符缺少 isSecured: false | 每个属性(顶层和嵌套属性)都必须带有 isSecured。请在每个描述符中包含该字段;若不需要受保护属性变体,默认设为 false。 |
objectType 被设为裸 referenceName 而非 FQN | 引用应使用 "<Package>.<referenceName>",不能只写 i_thing 或 e_thing。平台通过 FQN 解析;裸名称无法解析。 |
尝试自引用(objectType: "<Package>.<this_type>" 递归) | 自定义类型不能引用自身。请内联嵌套结构,并将深度限制为与生成引擎的递归上限一致的固定最大深度。使用生成器脚本维护内联副本。参见 references/type-definitions.md → 自定义类型不能自引用。 |
UI 组件通过 vars / inParams / outParams 中的 objectType: "<Package>.e_<enum>" 引用枚举时出现 Cannot find namespace '_types' 错误 | UI 组件的参数声明不能使用枚举 FQN 引用。请声明为底层基元类型(字符串值枚举使用 string,数值枚举使用 number),并在流程代码的使用位置进行类型转换(as $types.<Package>.e_<enum>)。流程层组件和其他自定义类型文件可以自由引用枚举。 |
| 未运行影响分析就收紧正在使用的类型;现在使用方在严格检查阶段失败 | 之前隐式获得 any 并读取新类型未声明字段的代码,会在平台的严格检查阶段出错。请撤销收紧操作,针对该类型的 FQN 调用 impact-analysis 技能,审查每个引用位置,并在收紧类型的同一次编辑中更新这些位置。 |
| 重命名正在使用的类型的属性,却未更新使用方 | 每个引用旧名称的位置都会失败。请先针对 <Package>.<referenceName> 调用 impact-analysis 技能,然后在同一次编辑中更新所有命中位置。 |
| 对 JSON 文件进行了美化格式化 | 磁盘上的类型定义文件是单行压缩 JSON,这与其他组件文件不同。不要美化格式;导入路径需要压缩格式。 |
根据其他类似 JSON 推断数值枚举的 isStringValue 应显式设为 false | 约定是字符串值枚举使用 true,数值枚举使用 null。false 是错误的。 |
误以为接口是开放结构,将其顶层 objectTypeDef 声明为 null | 接口使用 objectTypeDef: [...](描述符数组)。null 形式是接口的 enumTypeDef 槽位,不是其主体。 |
description 超过 100 个字符 | SQL 列有长度限制——推送将无法通过验证。请精简。 |
将 dxs configuration get -O envelope.json 的输出直接通过管道传给 dxs configuration upsert -D envelope.json | 这会静默破坏配置内容。编辑前务必先运行 jq .json envelope.json > body.json。参见上文“往返规则”。 |
编辑后,请调用 post-edit-verification,以发现描述、JSON 和架构违规。最终审查时,请调用 component-validator。