[英]How to type generic merging function in typescript
我有一个通用的 javascript function,我在 typescript 中无法正确输入。 它需要一系列项目。 如果这些项目是 arrays,则将它们展平为单个数组。 如果它们是对象,它将它们合并为一个大的 object。 否则它只返回原始数组。
function combineResults(responses) {
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;
}
}
是否可以安全地键入它,以便如果传递 arrays 数组,则返回类型将是数组,如果传递对象数组,则返回类型将是 object。 如果你传递一个既不是 arrays 也不是对象的数组,你的返回类型将是原始数组类型。
我倾向于给它一个与每种情况相对应的重载类型签名。 这里有一些障碍:一个是您只检查responses
的第一个元素并假设元素的 rest 属于同一类型; 但 arrays 可以是异构的。 由于 JS 和 TS 中的 arrays 被认为是对象,如果你给combineResults()
的调用签名提供一个像[[1, 2, 3], {a: 1}]
这样的异构数组,它可能会做一些奇怪的事情。 我不知道你想在运行时发生什么,所以我不知道你想在类型签名中看到什么。 这些是边缘情况。
另一个问题是像[{a: 1}, {b: ""}]
的数组在 TypeScript 中被认为是Array<{a: number, b?: undefined} | {b: string, a?: undefined}>
Array<{a: number, b?: undefined} | {b: string, a?: undefined}>
,并将其转换为{a: number, b: string}
涉及大量类型系统的跳跃,包括将联合转换为交集并过滤掉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;
}
}
调用签名应为 map 数组到数组,对象数组到 object 以及任何其他数组到自身。 让我们测试一下:
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;
} ?!?!? */
您可能希望调整这些签名以完全防止异构 arrays。 但那是另一个兔子洞,我现在没有时间 go 下来。
好的,希望有帮助; 祝你好运!
假设项目是object
,这个怎么样:
function combineResults(responses: Array<object> | Array<Array<object>>): Array<object> | object {
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.