ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

mobx-state-tree UnionOptions 详解:eager 与 dispatcher 如何决定联合类型的匹配策略

mobx-state-tree UnionOptions 详解:eager 与 dispatcher 如何决定联合类型的匹配策略 状态管理前端【免费下载链接】mobx-state-treeFull-featured reactive state management without the boilerplate项目地址https://gitcode.com/gh_mirrors/mo/mobx-state-tree点击查看免费下载UnionOptionsTypes是 mobx-state-tree 中types.union的配置接口用于控制联合类型在快照校验与节点实例化时的匹配策略。本文以该接口为骨架结合 union.ts 源码与 union.test.ts 测试用例深入讲解eager与dispatcher两个可选配置项的语义、默认值、底层实现与实战用法。读完本文你将能精确掌控联合类型在多候选匹配、歧义消除、复杂类型判别等场景下的行为写出类型安全且行为可预期的 MST 模型。一、UnionOptions 接口概览UnionOptions定义于 src/types/utility-types/union.ts#L26-L39在 src/index.ts#L72 中作为公开类型导出。它只有一个类型参数类型参数约束含义TypesIAnyType[]参与联合的候选类型数组IAnyType定义见 ianytype.md接口本身只包含两个可选属性属性类型默认行为说明dispatcherITypeDispatcherTypes不提供接收输入快照、返回应使用的具体类型eagerbooleantrue是否采用急切eager校验从源码构造函数可以看出默认值的落地方式options { eager: true, dispatcher: undefined, ...options } this._dispatcher options.dispatcher if (!options.eager) this._eager false即不传eager时等价于eager: truedispatcher缺省为undefined。在非生产环境devMode()下types.union还会对第一个参数做运行时断言要求必须是形如object { eager?: boolean, dispatcher?: Function }的普通对象见 union.ts#L196-L208并在 union.test.ts#L167-L175 中验证了误传裸函数会抛出 expected object。二、eager急切校验开关eager决定联合类型在校验validation阶段的匹配粒度接口注释给出的语义是当为true时采用第一个匹配的类型否则会检查所有候选类型只有当且仅当恰好一个类型匹配时校验才通过。底层实现isValidSnapshot 的两种分支在 union.ts#L129-L151 的isValidSnapshot中可以看到两种模式的完整逻辑const allErrors: IValidationError[][] [] let applicableTypes 0 for (let i 0; i this._types.length; i) { const type this._types[i] const errors type.validate(value, context) if (errors.length 0) { if (this._eager) return typeCheckSuccess() // eager命中即返回 else applicableTypes // 非 eager继续统计 } else { allErrors.push(errors) } } if (applicableTypes 1) return typeCheckSuccess() // 恰好一个匹配 return typeCheckFailure(context, value, No type is applicable for the union).concat( flattenTypeErrors(allErrors) )eager: true默认遍历候选类型时只要遇到第一个validate无错误的类型就立即判定成功不再检查后续类型。因此候选类型的声明顺序直接决定匹配结果。eager: false必须遍历完所有候选类型并统计匹配数applicableTypes 1才通过校验0 个或多个匹配都会失败失败信息统一为 No type is applicable for the union并附带展平后的各类型错误明细。测试用例印证union.test.ts#L49-L56 展示了eager: false下多个类型同时适用会抛错的行为const PlaneNotEager types.union({ eager: false }, Square, Box) expect(() { PlaneNotEager.create({ width: 2, height: 2 }) // 同时符合 Square 与 Box }).toThrow(/Error while converting/)而默认eager: true下顺序优先的语义由 union.test.ts#L116-L135 一组字面量测试直观体现const All types.model(All, { type: types.string }) const Man types.model(Man, { type: types.literal(M) }) // types.union(Man, Woman, All) 中 All 在最后所以 M 先命中 Man // types.union(All, Man) 中 All 在最前所以 M 被 All 抢走 expect(types.union(All, Man).create({ type: M })) // All.is(person) trueMan.is(person) false这提醒我们使用默认eager时更具体的类型必须放在更宽泛的类型之前否则宽泛类型会抢先匹配测试注释中的 All grabbed everything!。三、dispatcher显式指定匹配类型dispatcher是ITypeDispatcherTypes类型的函数接口注释定义为根据输入快照返回要使用的类型export type ITypeDispatcherTypes extends IAnyType[] ( snapshot: Types[number][SnapshotType] ) Types[number]即它接收快照SnapshotType返回联合中的某一个类型。这个签名在 docs/API/index.md 的ITypeDispatcher条目中有完整记载。底层实现determineType 的优先顺序在实例化instantiate、协调reconcile与校验isValidSnapshot三条路径上dispatcher 都拥有最高优先级。以 union.ts#L108-L127 的determineType为例if (this._dispatcher) { return this._dispatcher(value) // 1. dispatcher 优先 } if (reconcileCurrentType) { if (reconcileCurrentType.is(value)) { return reconcileCurrentType // 2. 协调时优先复用当前节点类型#1045 } return this._types.filter(t t ! reconcileCurrentType).find(type type.is(value)) } else { return this._types.find(type type.is(value)) // 3. 默认按顺序找第一个匹配 }instantiate创建determineType(initialValue, undefined)直接采用 dispatcher 返回的类型来实例化节点reconcile快照协调会传入current.getReconciliationType()先尝试 dispatcher再尝试复用当前节点类型这是 union.test.ts#L187-L317 中 #1045 场景修复的关键避免已有节点在applySnapshot时被错误切换类型isValidSnapshot校验只要配置了 dispatcher就直接委托this._dispatcher(value).validate(value, context)不再走 eager 的遍历逻辑。因此提供 dispatcher 后eager配置对校验分支不生效——两者是二选一的匹配手段dispatcher 显式指定eager 控制自动匹配的粒度。实战用法union.test.ts#L137-L176 给出了一个经典场景——按快照内容判别奇偶类型const Num types.union( { dispatcher: snapshot (snapshot.value % 2 0 ? Even : Odd) }, Even, Odd ) expect(Num.create({ value: 3 }).isOdd()).toBe(true) // 奇数 → Odd expect(Num.create({ value: 4 }).isEven()).toBe(true) // 偶数 → Evenunion.test.ts#L31-L35 则展示了按属性存在性判别const DispatchPlane types.union( { dispatcher: snapshot (snapshot height in snapshot ? Box : Square) }, Box, Square )复杂联合类型的坑与解法联合类型在复杂模型上可能产生幽灵匹配。文档 more-tips.md 的 Complex union types 一节给出了反例两个模型分别带foo: string[]与bar: number[]直接types.union(Foo, Bar)后期望得到{ foo: [test], bar: [200] }实际却得到{ foo: [test], foo: [] }。两种修复方式方式一dispatcher 显式判别const FooBar types.union( { dispatcher: snapshot { if (snapshot.foo) return Foo return Bar } }, Foo, Bar )方式二给基础模型加字面量标签const Foo types.model({ foo: types.array(types.string), type: types.literal(foo) }) const Bar types.model({ bar: types.array(types.number), type: types.literal(bar) })字面量标签配合默认 eager 模式即可让联合类型精确区分这也是 docs/overview/types.md#L51 中 types.literal与union组合非常强大 的原因。事实上types.enumeration本身就是字符串字面量联合的语法糖见 union.ts 中的说明与 index.md#L2446。四、union 函数签名与 UnionOptions 的传参位置UnionOptions作为types.union的第一个可选参数存在函数有两个重载union.ts#L175-L179export function unionTypes extends IAnyType[](...types: Types): IUnionTypeTypes export function unionTypes extends IAnyType[]( options: UnionOptionsTypes, ...types: Types ): IUnionTypeTypes调用时有两种形式// 形式一直接传候选类型 const Plane types.union(Square, Box) // 形式二先传 options再传候选类型 const DispatchPlane types.union( { dispatcher: snapshot ..., eager: false }, Box, Square )实现中通过isType(optionsOrType)区分两种形态第一个参数是类型则视为候选类型否则作为 options 处理union.ts#L191-L193。返回类型为IUnionTypeTypes其类型层面等价于候选类型各维度的联合CreationType / SnapshotType / TypeWithoutSTN 的_CustomCSProcessor规约见 union.ts#L162-L173。生成的名字形如(Square | Box)错误信息也会携带该描述。五、选择建议何时用 eager何时用 dispatcher结合 overview/types.md#L49 的官方概述与上述源码行为可归纳出如下决策路径场景推荐配置原因候选类型结构差异明显、可被快照形状区分如字面量标签、互斥字段默认eager: true把更具体的类型放前面无需额外代码声明顺序即匹配优先级候选类型存在重叠如{width}模型与{width, height}模型歧义必须被拒绝eager: false只有恰好一个类型匹配才通过避免静默错配候选类型靠形状无法区分或需要业务逻辑如奇偶、权限、版本号决定类型配置dispatcher显式、确定性地返回目标类型同时跳过遍历开销对既有节点做applySnapshot希望尽量保持原节点类型配置dispatcher或依赖 reconcile 的当前类型优先逻辑见 #1045 的修复路径union.ts#L118-L123最后提醒一点UnionOptions的两个属性都是可选的但二者存在协作与互斥关系——提供dispatcher时匹配结果完全由其决定eager仅作用于无 dispatcher 的自动匹配分支。理解这一点就能在设计可空联合、可辨识联合与多态模型时做出精确且可预期的类型决策。延伸阅读完整的union函数文档见 docs/API/index.md 的union条目IUnionType与ITypeDispatcher类型定义见 src/types/utility-types/union.ts更多联合类型实战与调试建议见 docs/tips/more-tips.md。赞分享状态管理前端【免费下载链接】mobx-state-treeFull-featured reactive state management without the boilerplate项目地址https://gitcode.com/gh_mirrors/mo/mobx-state-tree点击查看免费下载相关推荐mobx-state-tree CustomTypeOptions 详解用 types.custom 打造自定义序列化类型mobx state tree CustomTypeOptions 详解用 types.custom 打造自定义序列化类型 导读 CustomTypeOpti状态管理前端终极指南使用mobx-state-tree掌握复合、装饰与策略模式的状态模型设计终极指南使用mobx state tree掌握复合、装饰与策略模式的状态模型设计 mobx state tree是一个功能全面的响应式状态管理库它通过简洁的状态管理前端DeepChat 数据落盘位置权威指南从主数据库到加密元数据、备份布局与快照导入规则DeepChat 数据落盘位置权威指南从主数据库到加密元数据、备份布局与快照导入规则 本文基于 DeepChat 仓库中供数据导入工具使用的参考文档 data状态管理前端上一篇如何用Readest分屏阅读功能实现高效多文本对比下一篇探索未来视觉的钥匙Shader Park 开源项目完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表