繁体   English   中英

如何在 typescript 中键入通用合并 function

[英]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 下来。

好的,希望有帮助; 祝你好运!

Playground 代码链接

假设项目是object ,这个怎么样:

function combineResults(responses: Array<object> | Array<Array<object>>): Array<object> | object {

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM