如何验证键值为类类型的空Map是否符合指定类型?
解决空Map类型校验的问题
问题背景
定义了包含Map<SomeClass, SomeOtherClass>属性的myType类型,需要验证传入参数是否完全匹配该类型。现有方案先检查参数是否为Map实例,再验证首个键值是否为对应类实例,但空Map场景下存在缺陷:
- 若直接判定空Map合法,所有类型的空Map都会通过校验,无法区分
Map<SomeClass, SomeOtherClass>和其他类型的Map - 若不允许空Map,又不符合业务需求
需要实现空Map也能正确校验的方法,用于myValueObject值对象的构造校验,避免传入any类型参数,同时myType可能包含多个属性,需逐个校验并抛出精准错误信息。
解决方案
核心思路是给目标Map类型添加唯一类型标识,通过自定义子类继承原生Map,这样即使是空实例,也能通过instanceof直接判断类型;同时优化校验逻辑,逐个属性校验并输出明确错误。
1. 自定义Map子类
创建继承自原生Map的子类,用于标记特定的键值对类型:
class SomeClassMap extends Map<SomeClass, SomeOtherClass> {}
2. 更新类型定义
将myType中的index属性类型替换为自定义的SomeClassMap:
type myType = { index: SomeClassMap; };
3. 编写精准校验函数
使用asserts关键字让TypeScript自动收窄类型,逐个属性校验并抛出具体错误:
function validate(data: unknown): asserts data is myType { // 基础类型校验:必须是非空对象 if (typeof data !== 'object' || data === null) { throw new Error('传入参数必须是非空对象'); } // 校验index属性类型 const index = (data as myType).index; if (!(index instanceof SomeClassMap)) { throw new Error('index属性必须是SomeClassMap类型'); } // 非空时校验所有键值对(可选,根据业务需要是否开启全量校验) for (const [key, value] of index.entries()) { if (!(key instanceof SomeClass)) { throw new Error(`index中存在非法键:${key} 不是SomeClass实例`); } if (!(value instanceof SomeOtherClass)) { throw new Error(`index中存在非法值:${value} 不是SomeOtherClass实例`); } } // 若myType有其他属性,继续添加对应校验逻辑 // 示例:校验字符串类型的name属性 // const name = (data as myType).name; // if (typeof name !== 'string' || name.trim() === '') { // throw new Error('name属性必须是非空字符串'); // } }
4. 更新值对象构造函数
将构造函数参数类型改为unknown,确保所有传入参数都会经过校验:
class myValueObject { readonly value: myType; constructor(data: unknown) { validate(data); this.value = data; } }
方案优势
- 空Map校验可靠:自定义子类的空实例也能通过
instanceof准确识别,不会和其他类型的Map混淆 - 类型安全:校验函数使用
asserts,调用后TypeScript会自动将参数类型收窄为myType,无需额外断言 - 错误精准:逐个属性校验并输出具体错误信息,方便快速定位问题
- 扩展性强:新增
myType属性时,只需在校验函数中添加对应逻辑即可
备选方案(不推荐)
如果不想自定义子类,可以通过传入构造函数的方式校验,但空Map无法区分不同类型的Map,仅适用于对空Map类型区分要求不高的场景:
function isTypedMap<K, V>( obj: unknown, keyCtor: new (...args: any[]) => K, valueCtor: new (...args: any[]) => V ): obj is Map<K, V> { if (!(obj instanceof Map)) return false; // 空Map直接返回true,无法区分类型 if (obj.size === 0) return true; for (const [key, value] of obj) { if (!(key instanceof keyCtor) || !(value instanceof valueCtor)) { return false; } } return true; }
内容的提问来源于stack exchange,提问作者zlZimon
相关产品推荐
相关产品推荐

