javascript Typescript:子类型的动态类型

s3fp2yjn  于 2023-02-11  发布在  Java
关注(0)|答案(1)|浏览(138)

编写如下所示的groupingBy函数

function groupingBy<Item, Value, SeedKey extends string>(
  list: Item[],
  classifier: (item: Item) => string,
  mapper: (item: Item) => Value,
  seed: {[key in SeedKey]: Value[]}
): {[key in SeedKey]: Value[]} & {[key: string]: Value[] | undefined} {
  return list.reduce((accumulator, item) => {
    const key = classifier(item)
    // @ts-ignore (ignoring that we can't assign the return value of classifier to S since that's the point)
    ;(accumulator[key] = accumulator[key] || []).push(mapper(item))
    return accumulator
  }, seed)
}

这让我可以做的是:

const numbers = [1, 2, 3, 4, 10, 100, 44, 22]

const grouped = groupingBy(numbers, (num) => (num >= 10 ? 'big' : 'small'), identity, {big: []})
// grouped => { big: [10, 100, 44, 22], small: [1, 2, 3, 4] }
// {big: Value[]} & {[key: string]: Value[] | undefined})

/*
* Because seed was supplied with an object that has the 'big' key,
* we don't have to check for its presence.
* However, we do need to check for 'small'
*/

grouped.big.forEach((num) => assert(num > 10))
grouped.small?.forEach((num) => assert(num <= 10))

这很好用。问题是当种子太通用时:

const numbers = [10, 100, 44, 22]

const seed: {[key: string]: number[]} = {}

const grouped = groupingBy(numbers, (num) => (num >= 10 ? 'big' : 'small'), identity, seed)
// grouped => { big: [10, 100, 44, 22] }
// ({[key: string]: Value[]} & {[key: string]: Value[] | undefined})

/*
* Even though no specific keys were provided (instead *all* keys were provided),
* the result now allows unchecked access of all keys
*/

grouped.big.forEach((num) => assert(num > 10))
grouped.small.forEach((num) => assert(num <= 10)) // error!

有没有一种方法我可以检查种子并说:如果keyType ===字符串,那么returnType是{[key: string]: V[] | undefined},但是如果keyType扩展字符串,那么它是当前值?
我试过超载:

export function groupingBy<Item, Value>(
  list: Item[],
  classifier: (item: Item) => string,
  mapper: (item: Item) => Value,
  seed: {[key: string]: Value[]}
): {[key: string]: Value[] | undefined}
export function groupingBy<Item, Value, SeedKey extends string>(
  list: Item[],
  classifier: (item: Item) => string,
  mapper: (item: Item) => Value,
  seed: {[key in SeedKey]: Value[]}
): {[key in SeedKey]: Value[]} & {[key: string]: Value[] | undefined} {
  ...
}

但这只会导致返回类型始终为{[key: string]: Value[] | undefined}
谢谢你的帮助!

14ifxucb

14ifxucb1#

您可以尝试使用{[key: string]: V[] | undefined}{[key in S]: V[]}的并集类型,然后使用类型推断根据种子类型确定使用哪一个类型。

function groupingBy<T, V, S extends string>(
  list: T[],
  classifier: (item: T) => string,
  mapper: (item: T) => V,
  seed: {[key: string]: V[]} & {[key in S]: V[]}
): {[key: string]: V[] | undefined} & {[key in S]: V[]} {
  return list.reduce((acc, item) => {
    const key = classifier(item)
    // @ts-ignore (ignoring that we can't assign the return value of classifier to S since that's the point)
    ;(acc[key] = acc[key] || []).push(mapper(item))
    return acc
  }, seed)
}

这样,当你传入一个带有特定键type S的seed时,返回类型将是{[key in S]: V[]} & {[key: string]: V[] | undefined};当你传入一个带有{[key: string]: V[]}的泛型seed时,返回类型将是{[key: string]: V[] | undefined}

相关问题