TypeScript如何正确推断对象键类型?遍历数组类型推断问题
问题描述
我尝试通过遍历数组创建对象,仅选取所需元素,示例代码如下:
const items = [{ type: "foo" }, { type: "bar" }, { type: "baz" }] as const type MyItems = { foo: string | null bar: string | null } const myItems: MyItems = { foo: null, bar: null, } items.forEach(item => { if (item.type in myItems) { item.type // ^? (property) type: "foo" | "bar" | "baz" } if (Object.keys(myItems).includes(item.type)) { item.type // ^? (property) type: "foo" | "bar" | "baz" } })
实际场景更为复杂,无法直接使用reduce处理items。当前问题是经过条件判断后,TypeScript无法正确推断item.type的类型,我希望避免使用as断言,请问是否有其他解决方法?
解决方法
1. 自定义类型守卫函数
创建一个类型守卫函数,明确告知TypeScript判断逻辑对应的类型缩小规则,实现精准类型推断:
const items = [{ type: "foo" }, { type: "bar" }, { type: "baz" }] as const type MyItems = { foo: string | null bar: string | null } type MyItemKeys = keyof MyItems const myItems: MyItems = { foo: null, bar: null, } // 自定义类型守卫,确认type属于MyItems的键类型 function isMyItemKey(type: string): type is MyItemKeys { return type in myItems } items.forEach(item => { if (isMyItemKey(item.type)) { item.type // ^? (property) type: "foo" | "bar" // 此处可安全操作myItems[item.type] myItems[item.type] = "valid value" } })
2. 基于常量推导类型
如果目标对象结构固定,可以直接从常量推导类型,减少手动定义的冗余:
// 直接用const断言固定myItems结构,自动推导类型 const myItems = { foo: null, bar: null, } as const type MyItems = typeof myItems type MyItemKeys = keyof MyItems const items = [{ type: "foo" }, { type: "bar" }, { type: "baz" }] as const function isMyItemKey(type: string): type is MyItemKeys { return type in myItems } items.forEach(item => { if (isMyItemKey(item.type)) { item.type // 类型自动缩小为 "foo" | "bar" } })
3. 泛型封装处理逻辑(复杂场景适用)
将遍历逻辑封装为泛型函数,把必要的断言限制在函数内部,外部调用保持类型安全:
const items = [{ type: "foo" }, { type: "bar" }, { type: "baz" }] as const type MyItems = { foo: string | null bar: string | null } const myItems: MyItems = { foo: null, bar: null, } function processItems<T extends { type: string }, K extends string>( items: T[], target: Record<K, unknown>, handler: (item: T & { type: K }) => void ) { items.forEach(item => { if (item.type in target) { handler(item as T & { type: K }) // 内部断言,外部无需关注 } }) } // 调用时自动推导类型,无需额外断言 processItems(items, myItems, (item) => { item.type // 类型为 "foo" | "bar" myItems[item.type] = "processed content" })
内容的提问来源于stack exchange,提问作者Calvein
相关产品推荐
相关产品推荐

