如何将对象的键作为TypeScript联合类型约束函数参数?
解决方案:让TypeScript自动约束函数参数为对象的键
这个问题的核心是要保留对象的精确类型信息,同时让TypeScript自动推导键的联合类型,不用每次修改对象都手动更新类型约束。我们可以通过两个关键调整来解决:
1. 修正对象的类型定义,保留精确的键信息
你之前的代码里用as {[key: string]: number}做了类型断言,这直接抹掉了TypeScript对obj具体键的推断能力。去掉这个断言,让TypeScript自动推导对象的只读字面量类型:
// 去掉不必要的类型断言,让TypeScript自动推断精确类型 const obj = Object.freeze({ 'hello': 64, 'world': 20 }); // 此时 typeof obj 是 Readonly<{ hello: number; world: number }>
2. 改造pick函数,用keyof typeof动态约束参数
利用TypeScript的keyof操作符,结合typeof obj获取对象的键联合类型,把这个类型作为keys数组的元素类型。这样不仅编译时会检查传入的键是否合法,而且当你修改obj的键时,这个约束会自动同步更新:
const pick = (keys: Array<keyof typeof obj>): Array<number> => { const values = []; for (const key of keys) { // 运行时检查依然保留,应对可能绕过编译检查的场景 if (!(key in obj)) { throw new Error(`There's no such key: ${key}`); } values.push(obj[key]); } return values; };
测试效果
现在TypeScript会在编译阶段直接拦截无效键:
// 编译通过,完全合法 pick(['hello', 'world']); // 编译报错:类型'"test"'的参数不能赋给类型'"hello" | "world"'的参数 pick(['test']);
进阶优化:让返回值类型更精确
如果想让函数返回值的类型更贴合实际值(比如返回Array<64 | 20>而非Array<number>),可以用泛型结合索引访问类型:
const pick = <K extends keyof typeof obj>(keys: Array<K>): Array<typeof obj[K]> => { const values: Array<typeof obj[K]> = []; for (const key of keys) { if (!(key in obj)) { throw new Error(`There's no such key: ${key}`); } values.push(obj[key]); } return values; }; // 此时 pick(['hello']) 的返回类型是 Array<64>,pick(['hello', 'world']) 是 Array<64 | 20>
内容的提问来源于stack exchange,提问作者Simon
相关产品推荐
相关产品推荐

