实现 #3440 (PR #3445 )时顺带发现,不改 —— 与该单的 i18n 范围无关,单独记一笔。
packages/collaboration/src/PresenceAvatars.tsx 的状态圆点直接查一张按联合类型建的表:
const statusColors : Record < PresenceUser [ 'status' ] , string > = {
active : '#22c55e' ,
idle : '#f59e0b' ,
away : '#94a3b8' ,
} ;
// …
backgroundColor : statusColors [ user . status ] ,
若 user.status 落在 'active' | 'idle' | 'away' 之外,查表得 undefined,React 直接省略该行内样式 —— 圆点变成一个 8px 的白边透明圈,不是崩溃,但也不是任何一种「状态」。
为什么这不是纯理论 :presence 用户不是本包造的,而是宿主实现的 PresenceSource(PresenceProvider.tsx,WebSocket/SSE 一类传输)推进来的,subscribeTenant/subscribeRecord 的回调只做了 Array.isArray(next) 检查,没有逐条校验 status。类型标注管不到运行时的传输层。#3440 的单据正文本身就猜成了 online/away —— 恰好是一个会漏出联合类型的拼法。
PR #3445 在 tooltip 那一侧显式处理了这件事(未映射的 status 原样渲染成原始字符串,不臆造标签),但颜色这一侧仍无兜底。两侧口径不一:同一个未知 status,文字有话说,圆点没有。
为什么标 finding 而不是缺陷 :今天仓内没有任何 PresenceSource 实现(默认是 NOOP_SOURCE),所以现在没有用户撞得到;它随第一个真实 presence 传输接进来才会被激活。严重度交给 triage 判,不由发现者预判。
可能的修法(供 triage 参考,不代表已定):
A. 给 statusColors 加一个中性默认色(如 #cbd5e1),statusColors[user.status] ?? DEFAULT。最省事,但等于在消费端加宽容兜底 —— 与 contract-first 相冲。
B. 在 PresenceProvider 的订阅回调里把 status 归一化(未知值一律映到 away 或丢弃该用户),让联合类型在进入 React 树之前就真正成立。producer 侧收口,和 PR fix(collaboration,i18n): PresenceAvatars 的三处硬编码英文接入 i18n (#3440) #3445 的显示层兜底不冲突(那一处兜的是「显示原始值而非臆造」,不是「让非法值合法」)。
C. 什么都不做,等真实传输落地时连同校验一起设计。
我倾向 B:类型声明的联合应当由一处校验来兑现,而不是让每个下游查表点各自 ?? 一个默认值(那是第二份事实上的契约)。但这属于会改公开行为的取舍,不该由 #3440 顺手带上。
实现 #3440(PR #3445)时顺带发现,不改 —— 与该单的 i18n 范围无关,单独记一笔。
packages/collaboration/src/PresenceAvatars.tsx的状态圆点直接查一张按联合类型建的表:若
user.status落在'active' | 'idle' | 'away'之外,查表得undefined,React 直接省略该行内样式 —— 圆点变成一个 8px 的白边透明圈,不是崩溃,但也不是任何一种「状态」。为什么这不是纯理论:presence 用户不是本包造的,而是宿主实现的
PresenceSource(PresenceProvider.tsx,WebSocket/SSE 一类传输)推进来的,subscribeTenant/subscribeRecord的回调只做了Array.isArray(next)检查,没有逐条校验 status。类型标注管不到运行时的传输层。#3440 的单据正文本身就猜成了online/away—— 恰好是一个会漏出联合类型的拼法。PR #3445 在 tooltip 那一侧显式处理了这件事(未映射的 status 原样渲染成原始字符串,不臆造标签),但颜色这一侧仍无兜底。两侧口径不一:同一个未知 status,文字有话说,圆点没有。
为什么标 finding 而不是缺陷:今天仓内没有任何
PresenceSource实现(默认是NOOP_SOURCE),所以现在没有用户撞得到;它随第一个真实 presence 传输接进来才会被激活。严重度交给 triage 判,不由发现者预判。可能的修法(供 triage 参考,不代表已定):
statusColors加一个中性默认色(如#cbd5e1),statusColors[user.status] ?? DEFAULT。最省事,但等于在消费端加宽容兜底 —— 与 contract-first 相冲。PresenceProvider的订阅回调里把 status 归一化(未知值一律映到away或丢弃该用户),让联合类型在进入 React 树之前就真正成立。producer 侧收口,和 PR fix(collaboration,i18n): PresenceAvatars 的三处硬编码英文接入 i18n (#3440) #3445 的显示层兜底不冲突(那一处兜的是「显示原始值而非臆造」,不是「让非法值合法」)。我倾向 B:类型声明的联合应当由一处校验来兑现,而不是让每个下游查表点各自
??一个默认值(那是第二份事实上的契约)。但这属于会改公开行为的取舍,不该由 #3440 顺手带上。