发现于 #3808 的实施过程(给 element:text_input.defaultValue 补声明时撞上;那个 PR 按现有惯例选了一个 arm 并在 description 里写明另一个,没有改 ComponentInput)。未认领,交 PM triage。
这是 PR #3795 留下的那个 open question(「array/object 类型的 input 声明不了成员形状」)的联合类型孪生 :那一条是成员形状表达不了,这一条是同一层的类型本身 表达不了。
机制
ComponentInput.type 是单一粗类型(packages/types/src/base.ts:215):
type: 'string' | 'number' | 'boolean' | 'enum' | 'array' | 'object' | 'color' | 'date' | 'code' | 'file' | 'slot';
packages/sdui-parser/src/validate.ts 的 checkType(:106-142)按它逐一判定,不匹配就发 type-mismatch warning:
case 'string': … return typeof value === 'string' ? null : mismatch('a string');
case 'number': … return typeof value === 'number' ? null : mismatch('a number');
而 @objectstack/spec 里有一批键是联合 。声明时只能挑一个 arm,另一个 arm 的合法值就会被自己的 manifest 门报警。
今天就能撞到的标本(objectui origin/main @ c85268256)
键
spec 接受
已声明
被误报的合法写法
page:header.title
字符串 或 内联翻译映射
type: 'string'(containers.tsx:1584)
title={{ en: 'Account', 'zh-CN': '客户' }}
page:header.subtitle
同上
type: 'string'
同上
record:alert.title / .body
同上,description 原文就写着「Accepts an inline translation map ({ en, "zh-CN", … })」
type: 'string'
同上
page:card.title
同上
type: 'string'(containers.tsx:703)
同上
element:text_input.defaultValue
string | number(spec 实测)
type: 'string'(#3808 新增)
defaultValue={42},尤其配 inputType: 'number'
前四行尤其刺眼:input 自己的 description 教作者写内联翻译映射,同一个 input 的 type 又让 manifest 门对它报警 。一个平台权威的两半互相矛盾 —— 正是 #3797 / #3808 那条门要消掉的形状,只是这次矛盾在同一个 ComponentInput 里面。
触发面:kind:'jsx' 页面编译(packages/components/src/renderers/layout/page.tsx:462 用 getKnownTypes() + inputs 现搭 manifest)与 sdui.manifest.json 的保存门。severity 是 warning 不是 error,所以页面能编译、能渲染 —— 代价是这类 warning 变成噪音,而噪音会训练作者忽略真 的 unknown-prop。
几条可能的方向(不预设结论,改的是发布面契约)
(a) ComponentInput.type 收 string[] (或加 types?: ControlType[]),checkType 任一 arm 命中即通过。表达力对上了 spec,但 type 是 sdui.manifest.json 的已发布字段,读它的消费者(设计器面板、generateDts)都要跟。
(b) 加一个显式的 'i18nString' 粗类型 ,只覆盖「字符串或内联翻译映射」这一个高频模式,string | number 之类仍无解。窄、便宜、不通用。
(c) checkType 对已知的 i18n 形状放宽 (值是对象且键全为 locale 码时按字符串算)。不动契约,但在消费端加宽容 —— AGENTS.md #0.1 明确反对的方向,列在这里只为对照。
(d) 上游收窄 spec ,让这些键只接受一种形状,内联翻译走独立的键。最彻底,代价最大,而且内联翻译映射已有大量在野产出者。
按 #0.1 的取向 (a) / (d) 是「一个契约」方向,(c) 是造第二种方言;但 (a) 牵动已发布字段的形状,需要维护者定。
现状
#3808 的 PR 在 element:text_input.defaultValue 的注释里写明了这是一次有意识的收窄 并指向本单,description 里点出 number arm,没有在 checkType 侧加任何宽容。前四行标本是既有状态,本单不动它们,只把机制记档。
参考位置
packages/types/src/base.ts:215(ComponentInput.type)、packages/types/src/plugin-scope.ts:172(同一个类型的第二份声明,一起改)
packages/sdui-parser/src/validate.ts:106-142(checkType)
packages/components/src/renderers/layout/containers.tsx:1584(page:header.title)/ :703(page:card.title)
packages/plugin-detail/src/index.tsx —— record:alert.title / .body、element:text_input.defaultValue
apps/console/src/__tests__/registry-inputs-spec-parity.test.ts —— 两个方向的门都只比键名 ,类型宽窄它看不见(和 parity 门只比键名,retiredKey() 墓碑会被判成「spec 接受」—— 今天 pin 版零墓碑所以休眠,spec pin 一升就是门里的假绿(page:card.body 是现成标本) #3809 的墓碑盲区同一个"只比键名"根因)
关联:#3808 、#3407 / PR #3795 (成员形状的 open question)、#3797 / PR #3806 、#3809 、AGENTS.md #0.1
发现于 #3808 的实施过程(给
element:text_input.defaultValue补声明时撞上;那个 PR 按现有惯例选了一个 arm 并在 description 里写明另一个,没有改ComponentInput)。未认领,交 PM triage。这是 PR #3795 留下的那个 open question(「
array/object类型的 input 声明不了成员形状」)的联合类型孪生:那一条是成员形状表达不了,这一条是同一层的类型本身表达不了。机制
ComponentInput.type是单一粗类型(packages/types/src/base.ts:215):packages/sdui-parser/src/validate.ts的checkType(:106-142)按它逐一判定,不匹配就发type-mismatchwarning:而
@objectstack/spec里有一批键是联合。声明时只能挑一个 arm,另一个 arm 的合法值就会被自己的 manifest 门报警。今天就能撞到的标本(objectui
origin/main@c85268256)page:header.titletype: 'string'(containers.tsx:1584)title={{ en: 'Account', 'zh-CN': '客户' }}page:header.subtitletype: 'string'record:alert.title/.bodytype: 'string'page:card.titletype: 'string'(containers.tsx:703)element:text_input.defaultValuestring | number(spec 实测)type: 'string'(#3808 新增)defaultValue={42},尤其配inputType: 'number'前四行尤其刺眼:input 自己的 description 教作者写内联翻译映射,同一个 input 的
type又让 manifest 门对它报警。一个平台权威的两半互相矛盾 —— 正是 #3797 / #3808 那条门要消掉的形状,只是这次矛盾在同一个ComponentInput里面。触发面:
kind:'jsx'页面编译(packages/components/src/renderers/layout/page.tsx:462用getKnownTypes()+inputs现搭 manifest)与sdui.manifest.json的保存门。severity 是 warning 不是 error,所以页面能编译、能渲染 —— 代价是这类 warning 变成噪音,而噪音会训练作者忽略真的unknown-prop。几条可能的方向(不预设结论,改的是发布面契约)
ComponentInput.type收string[](或加types?: ControlType[]),checkType任一 arm 命中即通过。表达力对上了 spec,但type是sdui.manifest.json的已发布字段,读它的消费者(设计器面板、generateDts)都要跟。'i18nString'粗类型,只覆盖「字符串或内联翻译映射」这一个高频模式,string | number之类仍无解。窄、便宜、不通用。checkType对已知的 i18n 形状放宽(值是对象且键全为 locale 码时按字符串算)。不动契约,但在消费端加宽容 —— AGENTS.md #0.1 明确反对的方向,列在这里只为对照。按 #0.1 的取向 (a) / (d) 是「一个契约」方向,(c) 是造第二种方言;但 (a) 牵动已发布字段的形状,需要维护者定。
现状
#3808 的 PR 在
element:text_input.defaultValue的注释里写明了这是一次有意识的收窄并指向本单,description 里点出 number arm,没有在checkType侧加任何宽容。前四行标本是既有状态,本单不动它们,只把机制记档。参考位置
packages/types/src/base.ts:215(ComponentInput.type)、packages/types/src/plugin-scope.ts:172(同一个类型的第二份声明,一起改)packages/sdui-parser/src/validate.ts:106-142(checkType)packages/components/src/renderers/layout/containers.tsx:1584(page:header.title)/:703(page:card.title)packages/plugin-detail/src/index.tsx——record:alert.title/.body、element:text_input.defaultValueapps/console/src/__tests__/registry-inputs-spec-parity.test.ts—— 两个方向的门都只比键名,类型宽窄它看不见(和 parity 门只比键名,retiredKey()墓碑会被判成「spec 接受」—— 今天 pin 版零墓碑所以休眠,spec pin 一升就是门里的假绿(page:card.body是现成标本) #3809 的墓碑盲区同一个"只比键名"根因)关联:#3808、#3407 / PR #3795(成员形状的 open question)、#3797 / PR #3806、#3809、AGENTS.md #0.1