具有精确键值关联的TypeScript按属性分组

具有精确键值关联的TypeScript按属性分组,typescript,Typescript,我想要一个按函数分组的模型,其中分组类型是精确建模的。例如,在下面的例子中,很明显,tag.value.format()应该进行类型检查,因为我们是从release组开始工作的 const tags: ParsedTag[] = [ { type: 'release', value: Semver.parse('1.2.3')! }, { type: 'unknown', value: 'foobar' }, ] const tagGroups = groupByProp(tags

我想要一个按函数分组的模型,其中分组类型是精确建模的。例如,在下面的例子中,很明显,
tag.value.format()
应该进行类型检查,因为我们是从
release
组开始工作的

const tags: ParsedTag[] = [
  { type: 'release', value: Semver.parse('1.2.3')! },
  { type: 'unknown', value: 'foobar' },  
]

const tagGroups = groupByProp(tags, 'type')

// Goal: .format type checks
console.log(tagGroups.release.map(tag => tag.value.format()))

我试图构建这样一个函数,可以在中找到。

我找到了一个解决方案。守则全文如下:

type IndexableKeyTypes = string | number | symbol

type Indexable<T = unknown> = Record<string | number, T>

type JustIndexableTypes<T> = T extends IndexableKeyTypes ? T : never

type KeysMatching<Rec, Keys> = NonNullable<
  {
    [RecKey in keyof Rec]: Rec[RecKey] extends Keys ? RecKey : never
  }[keyof Rec]
>

type GroupBy<T extends Indexable, K extends IndexableKeys<T>> = {
  [KV in JustIndexableTypes<T[K]>]: Array<T extends Record<K, KV> ? T : never>
}

type IndexableKeys<Rec> = KeysMatching<Rec, IndexableKeyTypes>

export function groupByProp<Obj extends Indexable, KeyName extends IndexableKeys<Obj>>(
  xs: Obj[],
  keyName: KeyName
): GroupBy<Obj, KeyName> {
  type KeyValue = JustIndexableTypes<Obj[KeyName]>
  const seed = {} as GroupBy<Obj, KeyName>

  return xs.reduce((groupings, x) => {
    const groupName = x[keyName] as KeyValue

    if (groupings[groupName] === undefined) {
      groupings[groupName] = []
    }

    groupings[groupName].push(
      x as Obj extends Record<KeyName, KeyValue> ? Obj : never
    )

    return groupings
  }, seed)
}

//
// TEST
//

import * as Semver from 'semver'

type ParsedTag =
  | { type: 'unknown'; value: string }
  | { type: 'release'; value: Semver.SemVer }

const tags: ParsedTag[] = [
  { type: 'release', value: Semver.parse('1.2.3')! },
  { type: 'unknown', value: 'foobar' },  
]

const tagGroups = groupByProp(tags, 'type')

 // Goal: .format type checks
console.log(tagGroups.release.map(tag => tag.value.format()))
type IndexableKeyTypes=字符串|数字|符号
类型可索引=记录
类型JustIndexableTypes=T扩展可索引键类型?T:从来没有
类型KeysMatching=不可为null<
{
[RecKey in keyof Rec]:Rec[RecKey]扩展键?RecKey:从不
}[keyof Rec]
>
类型GroupBy={
[KV(单位:千伏)]:阵列
}
类型IndexableKeys=键匹配
导出函数groupByProp(
xs:Obj[],
keyName:keyName
):GroupBy{
类型KeyValue=JustIndexableTypes
常量seed={}作为GroupBy
返回x.reduce((分组,x)=>{
const groupName=x[keyName]作为KeyValue
if(分组[groupName]==未定义){
分组[groupName]=[]
}
分组[groupName]。推送(
当Obj扩展记录时的x?Obj:从不
)
返回分组
},种子)
}
//
//试验
//
从“Semver”导入*作为Semver
类型解析标记=
|{type:'unknown';值:string}
|{type:'release';值:Semver.Semver}
常量标记:ParsedTag[]=[
{类型:'release',值:Semver.parse('1.2.3')!},
{type:'unknown',value:'foobar'},
]
const tagGroups=groupByProp(标记“类型”)
//目标:。格式类型检查
log(tagGroups.release.map(tag=>tag.value.format())
可以找到此实例的链接

一开始我遇到的困难(引出原始问题)是:

type GroupBy={
[KV(单位:千伏)]:阵列
}
我意识到,通过迭代键控值类型,我可以在
T
上创建一个过滤器表达式


这个解决方案依赖于它们在联盟中是一个有区别的属性,这在我看来是合理和直观的。

请在您的问题中包含所有相关代码,而不是作为链接-并具体描述
groupByProp
函数应该做什么,除此之外,它应该进行类型检查。我已经澄清了这个问题。
type GroupBy<T extends Indexable, K extends IndexableKeys<T>> = {
  [KV in JustIndexableTypes<T[K]>]: Array<T extends Record<K, KV> ? T : never>
}