【问题标题】:how to type define a zip function in typescript如何在打字稿中定义一个zip函数
【发布时间】:2020-09-18 19:45:23
【问题描述】:

假设我要创建一个zip 函数:

function zip(arrays){
    // assume more than 1 array is given and all arrays 
    // share the same length
    const len = arrays[0].length;
    const toReturn = new Array(len);
    for (let i = 0; i < len; i++){
        toReturn[i] = arrays.map(array=>array[i]);
    }
    return toReturn;
}

console.log(zip([
    [1,2,3],
    [4,5,6],
    [7,8,9],
    [10,11,12],
]));
/*
Output:
(3) [Array(4), Array(4), Array(4)]
0: (4) [1, 4, 7, 10]
1: (4) [2, 5, 8, 11]
2: (4) [3, 6, 9, 12]
*/

为了在所有数组包含相同类型的元素时定义这个函数:

    function zip<T>(arrays: T[][]): T[][]{/* codes omited here */}

但是,当数组具有不同类型的元素时,我对如何使用泛型类型来完成类型定义感到困惑。

    const zipedResult = zip([[1,2,3],[true,false,true],['a','b','c']]);
    // error raises: Type 'false' is not assignable to type 'number'.(2322)

我想要的是

    [[1,2,3],[true,false,true],['a','b','c']]

可以自动推断为(number|boolean|string)[][] 而无需写入as (number|boolean|string)[][] 甚至 推断为[number[],boolean[],string[]]zip 的结果推断为[number, boolean, string][]

我应该如何正确键入define zip 来完成这些功能?

【问题讨论】:

  • iter-ops 具有 zip 运算符,无论同步还是异步都运行良好。而且,您可以检查其 TypeScript 声明 in the source ;)

标签: javascript typescript


【解决方案1】:

这是一个适合我的实现:

export function zip<T extends unknown[][]>(
  ...args: T
): { [K in keyof T]: T[K] extends (infer V)[] ? V : never }[] {
  const minLength = Math.min(...args.map((arr) => arr.length));
  // @ts-expect-error This is too much for ts
  return range(minLength).map((i) => args.map((arr) => arr[i]));
}

例如zip(["x", "y", "z"], [true, false, true]) 具有推断类型[string, boolean][]

【讨论】:

  • 当类型变得过于复杂而无用时,使用@ts-expect-error 作为救助也是一大优势
【解决方案2】:

我能看到的唯一方法是为您要处理的每个数组大小定义一个不同的 zip 函数(因为我们需要能够准确地说出 什么在zip结果的每个部分中):

const zip3 = <T, U, V>(arrays: [T[], U[], V[]]): [T, U, V][] => {
    const len = arrays[0].length;
    const toReturn: [T, U, V][] = new Array(len);
    for (let i = 0; i < len; i++){
        toReturn[i] = [arrays[0][i], arrays[1][i], arrays[2][i]];
    }
    return toReturn;
};

const result = zip3([
    [1,2,3],
    [true, false, true],
    [7,8,9],
]);

console.log(result);

希望有人可以进来并展示一个更好的方法来做到这一点,而不必根据您要压缩的数组数量重新定义函数;

【讨论】:

  • 我读过 lib.es2015.promise.d.ts,其中Promise.all 以类似的棘手方式定义。但是,我仍然认为将 zip 定义为 zip 并不是一个好主意。有没有其他方法可以同时处理非固定长度和非固定元素类型?
  • @TTY112358 我认为,是的,可能有;我正在重新审视这个,试图弄清楚它是否可能
  • @TTY112358 我认为这件事的难点在于我们不是试图返回任何T[],而是一些大小未知的元组,具有任意数量的不同类型
  • 我们以这种方式定义zip 怎么样:zip&lt;T extends any[]&gt;(arrays: HigherOrderArrayOf&lt;T&gt;):T[]。所以现在的问题是如何写HigherOrderArrayOf
【解决方案3】:

最常见的解决方案是:

declare type UnionTypes = number[] | string[] | boolean[];
function zip(arrays: UnionTypes[]): UnionTypes[]

【讨论】:

  • 恐怕返回的类型应该是 (number | string | boolean)[][] with const zipedResult = zip([[1,2,3],[true,false,true],['a','b','c']]); console.log(zipedResult); // [[1, true, "a"], [2, false, "b"], [3, true, "c" ]]
【解决方案4】:

我相信这是您看到 TS 的复杂性无用的少数情况之一。我只能考虑将类型放宽为 any 并在必要时进行手动类型检查。

function zip(arrays: any[][]): any[][]{
    // assume more than 1 array is given and all arrays 
    // share the same length
    const len = arrays[0].length;
    const toReturn = new Array(len);
    for (let i = 0; i < len; i++){
        toReturn[i] = arrays.map(array => array[i]);
    }
    return toReturn;
}

const zipedResult = zip([[1,2,3],[true,false,true],['a','b','c']]);
console.log(zipedResult); // [[1, true, "a"], [2, false, "b"], [3, true, "c" ]]

【讨论】:

    【解决方案5】:

    这是我想出的:

    • 完全输入
    • 接受不同类型的数组
    • 如果数组的长度不同,则结果将具有较短数组的长度(AFAIK 是 zip 的最常见实现)
    function zip<A, B>(as: A[], bs: B[]): [A, B][]
    {
        return as.length <= bs.length
             ? as.map((a, i) => [a, bs[i]])
             : bs.map((b, i) => [as[i], b])
    }
    

    【讨论】:

      猜你喜欢
      • 2018-12-27
      • 2019-01-23
      • 2023-04-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-03
      • 2022-01-09
      • 1970-01-01
      • 2021-12-23
      相关资源
      最近更新 更多