我倾向于给这个overloaded 类型签名对应于每个案例。这里有一些障碍:一个是您只检查responses 的第一个元素并假设其余元素属于同一类型;但数组可以是异构的。由于 JS 和 TS 中的数组被认为是对象,如果你给它一个像 [[1, 2, 3], {a: 1}] 这样的异构数组,combineResults() 的调用签名可能会做一些奇怪的事情。我不知道你想在运行时发生什么,所以我不知道你想在类型签名中看到什么。这些是边缘情况。
另一个问题是,像 [{a: 1}, {b: ""}] 这样的数组在 TypeScript 中被认为是 Array<{a: number, b?: undefined} | {b: string, a?: undefined}> 类型,并且将其转换为 {a: number, b: string} 涉及大量的类型系统跳跃,包括转换 unions to intersections 和过滤掉undefined 属性。
所以,这里是:
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
type Defined<T> = T extends any ? Pick<T, { [K in keyof T]-?: T[K] extends undefined ? never : K }[keyof T]> : never;
type Expand<T> = T extends infer U ? { [K in keyof U]: U[K] } : never;
function combineResults<T extends ReadonlyArray<any>>(responses: ReadonlyArray<T>): T[number][];
function combineResults<T extends object>(responses: ReadonlyArray<T>): Expand<UnionToIntersection<Defined<T>>>;
function combineResults<T extends ReadonlyArray<any>>(responses: T): T;
function combineResults(responses: readonly any[]) {
if (responses.length === 0) {
return [];
}
if (Array.isArray(responses[0])) {
return responses.flat(1);
} else if (typeof responses[0] === 'object') {
return Object.assign({}, ...responses);
}
else {
return responses;
}
}
调用签名应将数组数组映射到数组,将对象数组映射到对象,并将任何其他数组映射到自身。让我们测试一下:
const arrs = combineResults([[1, 2, 3], ["a", "b"]]); // (string | number)[]
const objs = combineResults([{ a: 1 }, { b: "hey" }]) // {a: number, b: string}
const nons = combineResults([1, 2, 3]); // number[]
看起来不错,我想。不过要注意边缘情况:
const hmm = combineResults([[1, 2, 3], { a: "" }])
/* const hmm: {
[x: number]: number;
a: string;
} ?!?!? */
您可能希望调整这些签名以完全防止异构数组。但这是另一个我现在没有时间去的兔子洞。
好的,希望对您有所帮助;祝你好运!
Playground link to code