如何在TypeScript中无类型转换地实现类型安全的带标记对象转换?
类型安全创建带标记对象的解决方案
你当前的实现用any绕过了TypeScript的类型检查,存在类型不安全的隐患。以下是几种优化方案,能让你在类型安全的前提下构建TaggedObject<T>:
方案一:Partial初始化 + 精准类型断言
将结果对象初始化为Partial<TaggedObject<T>>,让TypeScript跟踪每个键的类型,最后断言为完整类型(我们确保所有键都会被处理):
const createTaggedObject = <T extends object>(input: T): TaggedObject<T> => { const result: Partial<TaggedObject<T>> = {}; for (const key in input) { if (input.hasOwnProperty(key)) { const value = input[key]; if (typeof value === "string") { result[key] = { type: "string", value } as TaggedType<T[typeof key]>; } else if (typeof value === "number") { result[key] = { type: "number", value } as TaggedType<T[typeof key]>; } else if (typeof value === "boolean") { result[key] = { type: "boolean", value } as TaggedType<T[typeof key]>; } } } return result as TaggedObject<T>; };
方案二:拆分单个值标记逻辑为辅助函数
用泛型辅助函数处理单个值的标记,让TypeScript更精准地推断类型,避免直接断言整个对象:
const tagValue = <T extends string | number | boolean>(value: T): TaggedType<T> => { switch (typeof value) { case "string": return { type: "string", value } as TaggedType<T>; case "number": return { type: "number", value } as TaggedType<T>; case "boolean": return { type: "boolean", value } as TaggedType<T>; default: throw new Error(`不支持的类型: ${typeof value}`); } }; const createTaggedObject = <T extends { [K in keyof T]: string | number | boolean }>(input: T): TaggedObject<T> => { const result: Partial<TaggedObject<T>> = {}; for (const key in input) { if (input.hasOwnProperty(key)) { result[key] = tagValue(input[key]); } } return result as TaggedObject<T>; };
方案三:自定义类型守卫强化类型检查
通过自定义类型守卫函数,让TypeScript明确每个分支中值的类型,进一步提升类型安全性:
function isString(value: unknown): value is string { return typeof value === "string"; } function isNumber(value: unknown): value is number { return typeof value === "number"; } function isBoolean(value: unknown): value is boolean { return typeof value === "boolean"; } const createTaggedObject = <T extends object>(input: T): TaggedObject<T> => { const result: Partial<TaggedObject<T>> = {}; for (const key in input) { if (input.hasOwnProperty(key)) { const value = input[key]; if (isString(value)) { result[key] = { type: "string", value }; } else if (isNumber(value)) { result[key] = { type: "number", value }; } else if (isBoolean(value)) { result[key] = { type: "boolean", value }; } } } return result as TaggedObject<T>; };
关键说明
- 用
Partial<TaggedObject<T>>初始化结果,避免any导致的类型丢失。 - 最终的
as TaggedObject<T>断言是安全的,因为我们遍历了输入对象的所有自有属性,且只处理string/number/boolean类型的属性。 - 若要处理其他类型属性,可添加错误逻辑,避免生成不符合类型定义的结果。
内容的提问来源于stack exchange,提问作者lentil-soup
相关产品推荐
相关产品推荐

