Object.seal 与严格模式配合可守住状态机结构边界:封禁新增/删除字段、保留合法值更新,并通过 TypeScript 和 ESLint 实现编译期拦截,辅以运行时断言确保密封生效。

Object.seal 本身不参与编译期检查,它只在运行时生效;所谓“编译运行期双重拦截”,本质是开发阶段靠工具链提前发现风险 + 运行阶段靠 seal + 严格模式兜底拦截非法操作。重点不是 seal 多强大,而是它和严格模式配合后,能把“误操作”变成可捕获的错误,而不是静默失败。
为什么 seal + 严格模式能守住状态机结构边界
状态机的核心是有限、预定义的状态集合(如 'idle' | 'loading' | 'success' | 'error')和受控的迁移路径。向其注入未声明的新状态(如 state.status = 'corrupted' 或 state.customFlag = true),会破坏状态一致性。seal 的作用正是封住这个入口:
- 它让状态对象不可扩展(不能加新字段)
- 所有自有属性设为
configurable: false(不能删、不能改描述符) - 但保留
writable: true(允许合法状态值更新,比如state.status = 'loading') - 严格模式下,任何违反 seal 规则的操作(如新增属性、删除属性、重定义属性)都会立即抛出
TypeError,而非静默忽略
这比非严格模式下“什么也不报、什么也不做”可靠得多。
在状态机初始化时密封,而不是事后补救
必须在状态对象创建完成、所有初始字段赋值完毕后,立刻 seal。延迟密封等于留出窗口期,业务代码可能已写入非法字段。推荐封装初始化函数:
function createStateMachine(initial) {
const state = { ...initial };
Object.seal(state);
return state;
}
// 使用
const machine = createStateMachine({
status: 'idle',
data: null,
error: null
});这样确保从诞生起就结构受控。
深度密封嵌套状态字段(如 state.context)
seal 是浅层操作。如果状态机含嵌套对象(例如 state.context = { userId: 123, role: 'admin' }),该子对象本身仍可被任意修改。需主动递归处理:
用于 inference.sh 的 JavaScript/TypeScript SDK,可运行 AI 应用、构建代理、集成 150+ 模型。包名:@inferencesh/sdk(npm install),完整 TypeScript 支持。
function deepSeal(obj) {
if (obj != null && typeof obj === 'object' && !Object.isFrozen(obj)) {
Object.seal(obj);
Object.values(obj).forEach(deepSeal);
}
return obj;
}
const machine = deepSeal({
status: 'idle',
context: { userId: 123 },
meta: { timestamp: Date.now() }
});否则 machine.context.newField = true 依然成功——seal 没管到那一层。
配合 TypeScript 和 ESLint 实现编译期拦截
seal 解决不了“写错字段名”这类问题,但类型系统可以:
- TypeScript 接口或
type State明确限定允许的键名和值类型 - ESLint 插件(如
@typescript-eslint/no-unsafe-assignment)在编辑器里标红state.invalidKey = 'xxx' - 自定义 lint 规则可检测对 sealed 对象的
delete或defineProperty调用
这是真正的“编译期拦截”:不等运行,代码就过不了 CI。
运行时主动校验与防御性断言
不要假设 seal 一定被调用。在关键路径(如状态更新函数入口)加断言:
function updateStatus(machine, newStatus) {
if (!Object.isSealed(machine)) {
throw new Error('State machine must be sealed before use');
}
if (!['idle', 'loading', 'success', 'error'].includes(newStatus)) {
throw new Error(`Invalid status: ${newStatus}`);
}
machine.status = newStatus; // ✅ 允许
}这样即使 seal 被漏掉,也能在启动时暴露问题。
不复杂但容易忽略

















