要理解我的问题,首先让我解释一下我想要实现的目标。
我编写了以下groupBy
函数:
const groupBy = <T extends Record<keyof T, unknown>>(arr: T[], key: keyof T) => {
return arr.reduce((prev, { [key]: k, ...rest }) => {
if (!prev[k]) prev[k] = [] as Omit<T, keyof T>[];
prev[k].push(rest);
return prev;
}, {} as Record<keyof T, Omit<T, keyof T>[]>);
};
现在,假设我有以下界面:
type MyType = {
city: number,
name: string
}
以及以下列表:
const L: MyType[] = [
{
city: 1,
name: "name1"
},
{
city: 1,
name: "name2"
},
{
city: 2,
name: "name3"
},
{
city: 2,
name: "name4"
}
]
当我像这样调用函数时:
const grouped = groupBy(L, "city")
我希望grouped
的类型如下:
Record<number, Omit<MyType, "city">[]>
即,传递给 groupBy
函数的键应该从数组项的类型中省略。
我怎样才能实现这个目标?这是可以实现的吗?
最佳答案
是的,这是可能的。由于我们需要确保键的类型("city"
)既是对象的键又是PropertyKey
(所以我们可以使用它作为 Record
的键),我认为我们必须使用这样的条件类型:
type GroupByResult<ObjectType, KeyType extends keyof ObjectType> =
ObjectType[KeyType] extends PropertyKey
? Record<ObjectType[KeyType], Omit<ObjectType, KeyType>[]>
: never;
那么函数的类型是:
export const groupBy = <ObjectType, KeyType extends keyof ObjectType>(
arr: ObjectType[],
key: KeyType
): GroupByResult<ObjectType, KeyType> => {
// ...
};
// ...
const grouped = groupBy(L, "city")
// ^? −− const grouped: Record<number, Omit<MyType, "city">[]>
注意:reduce
调用中存在类型错误。我没有尝试解决这些问题,因为我认为您的问题是关于返回类型的。如果您还需要这些方面的帮助,请告诉我(我怀疑您在实现中总是需要一些类型断言,但我可能是错的)。
但我确实注意到实现中存在一个错误:在索引到 prev
时,您正在使用 key
,而您应该使用 k
, reduce
回调的主体应该是:
if (!prev[k]) prev[k] = [];
prev[k].push(rest);
return prev;
Playground link使用 any
绕过内部类型错误(对于像 groupBy
这样的小型、独立、易于测试的函数来说,这通常足够好)。
关于typescript - 从类型中动态排除某个键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73924450/