打字稿:提取嵌套键

时间:2018-08-02 08:05:10

标签: typescript generics types

这是一种扁平化(如果我不混淆术语的话)功能。应该用什么代替问号?还是我这样做完全错误?

const array = [{foo: 'val1'}, {bar: 'val2'}]

function flatten <ARRAYOFOBJECTS extends object[]>(array: ARRAYOFOBJECTS): Flat<ARRAYOFOBJECTS> {
  // implementation here, does not matter, all that matters is that it returns following:
  return {foo: 'val1', bar: 'val2'}
}

type Flat<ARRAYOFOBJECTS> = {
  [K in keyof ARRAYOFOBJECTS[????]]: string
}

flatten(array). // at this point IDE should suggest two values: "foo" and "bar"

1 个答案:

答案 0 :(得分:1)

您只需声明数组项类型的类型参数,然后将其返回为函数的返回值,由于typescript推断数组类型的方式(对于{ foo: string; bar?: undefined; } | { bar: string; foo?: undefined;})[])。但是,要确保正确统一所有联合,我们将需要UnionToIntersection

上的此答案提供一些额外的帮助
type UnionToIntersection<U> = (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never

const array = [{ foo: 'val1' }, { bar: 'val2' }]

function flatten<T> (array: T[]) : UnionToIntersection<T> {
// implementation here, does not matter, all that matter is that it returns following:
return {foo: 'val1', bar: 'val2'} as any
}

// at this point IDE suggests two values: "foo" and "bar"
flatten(array).bar 
flatten(array).foo 

修改

使用UnionToIntersection<T>的原因是因为数组项的类型很大程度上取决于您如何定义数组:

例如:

const array1 = [{foo: 'val1'}, {bar: 'val2'}] // { foo: string; bar?: undefined; } | { bar: string; foo?: undefined;})[]`)

const foo = { foo: 'val1' }
const bar = { bar: 'val2' }
const array2 = [foo, bar]// ({ foo: string; } | { bar: string; })[]

对于array1打字稿,会将所有对象文字的属性添加到联合中的所有类型,我们在联合的每个成员上都获得了一堆类型为undefined的额外可选属性({{1} })这就是为什么我们可以访问任何成员,即使它们不在数组的所有项目中也是如此。

对于{ foo: string; bar?: undefined; },每个项目的类型都是已知的(即在数组定义之前确定),因此,打字稿不会添加这些额外的属性,因此我们得到了一个没有通用成员的并集作为数组(array2),我们将无法访问任何成员。