我有一个名为IndexedObject
的type
,它被定义为:
type IndexedObject<T extends {} = { [k: string]: any }> = T & { _i: number }
// It just appends an index "_i" to the object
// The "_i" index is assigned in the first function of the chain,
// and then it doesn't get changed anywhere else
我使用它的原因是,我有一个输入数组,以及几个转换和过滤它的函数,我需要跟踪每个元素的原始索引(这在主要问题中可能不重要,但我解释了它,以防万一(
因此,我有任意数量的IndexedObject<T1>[]
、IndexedObject<T2>[]
等类型的数组,其中T1
、T2
等是具有任意键的不同对象类型。我想把它们合并成一个合并对象的数组。例如:
interface T1 {
id: number
name: string
}
interface T2 {
status: boolean
message: string
}
let arr1: IndexedObject<T1>[] = [
{_i: 0, id: 201, name: 'a'},
{_i: 3, id: 5, name: 'h'}
]
let arr2: IndexedObject<T2>[] = [
{_i: 0, status: true, message: '1234'},
{_i: 1, status: false, message: '5678'},
{_i: 4, status: false, message: '9065'}
]
// merged:
let merged = [
{_i: 0, id: 201, name: 'a', status: true, message: '1234'},
{_i: 1, status: false, message: '5678'},
{_i: 3, id: 5, name: 'h'},
{_i: 4, status: false, message: '9065'}
]
合并本身不是问题。这是我写的函数:
function mergeIndexedArrays(...arrays: IndexedObject[][]) {
return arrays.reduce((result, array) => {
for (let a of array) {
result[a._i] = { ...result[a._i], ...a }
}
return result
}, [])
}
问题出在打字上。我需要merge函数的返回类型来动态地包含所有输入类型的键(这里是T1
和T2
(。我该怎么做?
请注意,有任意数量的数组。
我试着用variadic tuple types
来实现这一点,但我想不通。
谢谢
本质上,我们想要"提取物";索引对象(infer
(中的所有类型,然后将它们相交在一起(并使结果为Partial,因为它们不能保证存在(。
type UnionToIntersection<U> =
(U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never;
type InferTypes<A> = A extends IndexedObject<infer U>[][] ? U : never;
function mergeIndexedArrays<Arrays extends IndexedObject[][]>(...arrays: Arrays): Partial<UnionToIntersection<InferTypes<Arrays>>>[];
function mergeIndexedArrays(...arrays: IndexedObject[][]) {
return arrays.reduce((result, array) => {
for (let a of array) {
result[a._i] = { ...result[a._i], ...a }
}
return result
}, [])
}
然后我们可以使用rest参数将所有数组收集到泛型类型Arrays
中。此外,我已经将签名移动到重载中,这样它就不会在函数体中导致令人讨厌的类型错误。
游乐场