如何在TypeScript中强制Map包含字面量联合类型的所有键?
实现强制包含指定键的Map类型约束
在TypeScript中,原生Map<K, V>类型不会强制要求包含K联合类型的所有成员作为键。要实现类似Record<K, V>对对象的约束效果,可以通过交叉类型重写get方法的返回值类型,配合工厂函数强制初始化所有键来完成。
步骤1:定义自定义Map类型
创建一个交叉类型,将原生Map与重写get方法的类型结合,确保调用get获取联合类型中的任意键时,返回值是确定的V而非V | undefined:
type MyMapType<K extends string | number | symbol, V> = Map<K, V> & { get(key: K): V; };
步骤2:用工厂函数强制初始化所有键
直接声明MyMapType类型的变量无法在编译阶段检查是否缺键,因此需要一个工厂函数,利用Record<K, V>的约束确保输入对象包含所有必填键,再转换为Map实例:
function createRequiredMap<K extends string | number | symbol, V>(record: Record<K, V>): MyMapType<K, V> { const entries = Object.entries(record) as [K, V][]; return new Map(entries) as MyMapType<K, V>; }
示例使用
定义字符串字面量联合类型,通过工厂函数创建符合约束的Map:
// 定义必填键的联合类型 type TNumberNames = 'one' | 'two' | 'three'; // 正确:包含所有必填键,编译通过 const numberMap = createRequiredMap<TNumberNames, number>({ one: 1, two: 2, three: 3 }); // 错误:缺少'three'键,TypeScript编译报错 const invalidMap = createRequiredMap<TNumberNames, number>({ one: 1, two: 2 }); // 使用时,get方法返回确定的number类型(而非number | undefined) const oneValue = numberMap.get('one'); // 类型为number
原理说明
MyMapType通过交叉类型覆盖了原生Map.get的返回类型,消除了undefined的可能性,从类型层面确保键必然存在。- 工厂函数
createRequiredMap利用Record<K, V>的强约束,强制输入对象包含所有K的成员,间接保证生成的Map实例拥有所有必填键。
内容的提问来源于stack exchange,提问作者Hwi-yong Eom
相关产品推荐
相关产品推荐

