编写如下所示的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}
谢谢你的帮助!
1条答案
按热度按时间14ifxucb1#
您可以尝试使用
{[key: string]: V[] | undefined}
和{[key in S]: V[]}
的并集类型,然后使用类型推断根据种子类型确定使用哪一个类型。这样,当你传入一个带有特定键
type S
的seed时,返回类型将是{[key in S]: V[]} & {[key: string]: V[] | undefined}
;当你传入一个带有{[key: string]: V[]}
的泛型seed时,返回类型将是{[key: string]: V[] | undefined}
。