如何在不使用类型断言的情况下编写类型安全的TypeScript stringifyQuery函数
问题分析
TypeScript 报错的核心原因是:Object.entries(query) 返回的类型是 Array<[string, T[keyof T]]>,经过 map 转换后得到 Array<[string, string]>,而 Object.fromEntries 会将其推断为 { [k: string]: string }——这是一个键为任意字符串的对象类型,但我们的返回类型要求是与输入对象 T 的键完全对应的映射类型 { [K in keyof T]: string },两者类型不兼容。
下面提供几种无需使用不安全类型断言的解决方案:
方案一:手动遍历键并赋值
通过将 Object.keys 的结果转换为 keyof T 类型数组(这是安全的,因为 T 的键必然是字符串子集),逐个赋值确保类型匹配:
function stringifyQuery<T extends Record<string, unknown>>(query: T): { [K in keyof T]: string } { const result = {} as { [K in keyof T]: string }; const keys = Object.keys(query) as readonly (keyof T)[]; for (const key of keys) { result[key] = JSON.stringify(query[key]); } return result; }
方案二:使用 reduce 约束类型
利用 reduce 逐步构建结果对象,同时明确键的类型转换:
function stringifyQuery<T extends Record<string, unknown>>(query: T): { [K in keyof T]: string } { return Object.keys(query).reduce((acc, key) => { const typedKey = key as keyof T; acc[typedKey] = JSON.stringify(query[typedKey]); return acc; }, {} as { [K in keyof T]: string }); }
方案三:封装类型安全的 fromEntries
如果需要复用类似的类型转换逻辑,可以先封装一个保留键类型的 fromEntries 工具函数:
// 定义保留键具体类型的对象条目类型 type ObjectEntries<T> = { [K in keyof T]: [K, T[K]] }[keyof T][]; function typeSafeFromEntries<T extends Record<string, unknown>>(entries: ObjectEntries<T>): T { return Object.fromEntries(entries) as T; } function stringifyQuery<T extends Record<string, unknown>>(query: T): { [K in keyof T]: string } { const stringEntries = (Object.entries(query) as ObjectEntries<T>).map( ([key, value]) => [key, JSON.stringify(value)] as const ); return typeSafeFromEntries(stringEntries); }
以上方案中出现的类型转换均为基于已知类型关系的安全转换,而非随意的类型断言,既满足了编译时的类型安全要求,也保证了运行时的行为符合预期。
内容的提问来源于stack exchange,提问作者leafmeal
相关产品推荐
相关产品推荐

