打字稿通过鉴别器属性将鉴别联合类型的数组分组为记录

叶夫根尼·克拉西克

我正在尝试编写通用“groupBy”函数的打字稿签名,该函数会将可区分类型联合数组“传播”到记录中,其中记录的每个字段都是可能的鉴别器值并指向具体对象的数组从联合中输入。

例子:

interface Dog {
  type: 'dog'
  dogMetadata: {}
}

interface Cat {
  type: 'cat'
  catMetadata: {}
}

type Animal = Dog | Cat

const animals: Animal[] = [{ type: 'dog', dogMetadata: {} }, { type: 'cat', catMetadata: {} }]

每个接口都有一个共同的鉴别器属性,没有其他共同的属性。

这是简单的“groupBy”签名,它不传播类型联合值,迫使我向下转换记录的值:

function groupBy<T, K extends string>(arr: T[], keyExtractor: (element: T) => K): Record<K, T[]>

const animalsByType: Record<'dog' | 'cat', Animal[]> = groupBy(animals, it => it.type)
const dogs: Dog[] = animalsByType['dog'] as Dog[]  // Must downcast Animal[] to Dog[]

我怎样才能创建一个知道区分联合类型的具体类型的“groupBy”?我想要这样的东西:

const animalsByType: { dog: Dog[], cat: Cat[] } = groupBy(animals, it => it.type)
const dogs: Dog[] = animalsByType['dog']  // animalsByType.dog is known to be Dog[] by typescript

实现很简单,Typescript 部分有问题:) 我正在寻找一个不做假设的通用解决方案,比如鉴别器属性的名称或类型联合中的类型数量。

后续问题

当联合嵌套在另一个类中时,是否可以使相同的签名起作用?

interface Holder<T> {
  data: T
}

const animalHolders: Holder<Animal>[] = animals.map(data => ({ data }))

const dogHolders: Holder<Dog> = groupBy(animalHolders, it => it.data.type) // Any way of doing this?

游乐场链接

谢谢您的帮助。

花费者

好问题...

让我们首先创建一些实用程序类型:

type KeysOfType<O, T> = {
  [K in keyof O]: O[K] extends T ? K : never;
}[keyof O];

这会将该点的所有键提取O到 type 的值T这将用于将判别式的类型限制为string类型。它们将用作输出类型中的键,因此我们对允许其他类型的判别式并不真正感兴趣。

让我们还添加Expand<T>以使我们的结果类型在智能感知中看起来更好。

type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;

现在,让我们创建一个表示groupBy函数返回类型的类型:

type Return<T, K extends KeysOfType<T, string>> = 
    { [KK in string & T[K]]: { [_ in K]: KK } & T }

或者,可选地,自由地应用上述Expand<T>类型为消费者提供更好的智能感知:

type Return<T, K extends KeysOfType<T, string>> = 
    Expand<{ [KK in string & T[K]]: Expand<{ [_ in K]: KK } & T> }>    

所以现在我们可以声明函数:

function groupBy<T, K extends KeysOfType<T, string>>(
    arr: T[], 
    keyExtractor: (element: T) => T[K]): Return<T, K>{
    throw Error();
}

并称之为:

const groups = groupBy(animals, e => e.type)

为了完全类型安全,无论选择哪个鉴别器属性。

游乐场链接

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章