You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 10:57:14