如何根据类型化对象数组中的键推断类型?

How can infer a type based on a key from an array of typed objects?

我正在尝试使用其中一个重复出现的(和必需的)键创建一个基于对象数组的类型,但是 typescript 没有按照我期望的方式处理 Array.map()

const list = [
  {
    _id: 'foo1'
  },
  { _id: 'foo2' }
] as const

const listIds = list.map(listValue => listValue._id)

我希望 listIds 被推断为 ['foo1', 'foo2'],但被视为 ("foo1" | "foo2")[]。这对我来说没有意义,因为 Array.map 到 return 一个与原始数组大小不同的数组是不可能的。

还有其他方法可以获得我正在寻找的结果吗?

PS。请参阅 TypeScript Playground 示例 here

对于获取特定键值的这种特定情况,可以使用辅助函数:

const mapByKey = <A extends readonly unknown[], K extends keyof A[number]>(
  array: A,
  key: K
// the -readonly ensures the return type isn't readonly even if the
// input array is
): {-readonly [P in keyof A]: A[P][K]} =>
  array.map((x: A[number]) => x[key]) as {-readonly [P in keyof A]: A[P][K]}

// ['foo1', 'foo2']
const listIds = mapByKey(list, '_id')

Playground link


但是,我认为没有一种简单的方法可以将此概括为任何类型的函数。

我们真正想要的:

declare const map: <A extends readonly unknown[], F>(
  array: A,
  fn: <T extends A[number]>(x: T) => F<T>
): {-readonly [K in keyof A]: F<A[K]>}
// with your example F would be like a generic type like
// type F<T> = T['_id']

但是,这不会编译为 TypeScript lacks higher-kinded types。在 TypeScript 中有各种 hacks 可以模拟这个,但它们使用起来很麻烦,我个人认为这不值得解决这个问题。