首页 > 解决方案 > 在打字稿中接受调用者的哪个数组方法

问题描述

背景:

以下功能之间的唯一区别是:

const createGrid = <T>(width: number, height: number, callback: (x: number, y:number) => T) =>
  Array
    .from(Array(width).keys())
    .map(
      (x: number) => Array
        .from(Array(height).keys())
        .map((y: number) => callback(x, y))
    );

const visitGridCoordinates = (width: number, height: number, callback: (x: number, y:number) => void) =>
  Array
    .from(Array(width).keys())
    .forEach(
      (x: number) => Array
        .from(Array(height).keys())
        .forEach((y: number) => callback(x, y);)
    );

问题:

有没有办法在这些上创建一个包装函数,将哪个方法用作参数?我尝试了几件事,但一直遇到问题。

const createGrid = verbGrid<string>(Array.prototype.map); // or ('map') alternately
const visitGridCoordinates = verbGrid(Array.prototype.forEach); // or ('forEach') alternately

const verbGrid = ???

我尝试编写这样的函数,然后使用“从用法推断参数类型”,结果几乎可以工作:

const verbGrid = <U>(arrayMethod: (callbackfn: (value: any, index: number, array: any[]) => void, thisArg?: any) => void) => (
  width: number,
  height: number,
  callback: ((x: number, y:number) => U) | ((x: number, y:number) => boolean)
) => {
  const result: void | U[][] = arrayMethod.call(
    Array.from(Array(width).keys()),
    (x: number) => arrayMethod.call(
      Array.from(Array(height).keys()),
      (y: number) => callback(x, y)
    )
  )
  return result;
};

但是当我尝试使用createGrid它时,它的返回类型总是只有void. 当然是,因为类型arrayMethod说它返回void。当我尝试将联合类型用于整个类型描述符或仅用于返回值时,会发生各种不同的错误。

我尝试使用varofas invarof Array<number>["map"] | varof Array<number>["forEach"]来更明确地使用类型,并避免重新描述这两种方法类型。但是我仍然没有运气(我注意到map版本没有指定U任何地方,我不知道该怎么做)。

这甚至可能吗?如果是这样,你介意帮助我了解我哪里出错了吗?

我得到的各种结果:

我试图简化一些事情,如下所示:

type ValueReturning = <T, U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any) => U[];
type VoidReturning = <T>(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any) => void;

class Enumerable {
  static range(start: number, count: number) {
    return Array.from(Array(count).keys()).map(index => start + index);
  }
}

但这根本不起作用(“类型'ValueReturning'不是通用的”):

const verbGrid = <U>(arrayMethod: ValueReturning<number, U> | VoidReturning<number>) => (

感谢任何指导。

注意:我是 TypeScript 的新手,但不是泛型的新手(来自 C#)。

附录

如果有帮助,这里是这两个函数的调用站点。

this._grid = createGrid(width, height, (x: number, y: number) => {
  const cell = new Cell<string>(x, y);
  this._cells.add(cell);
  return cell;
});
visitGridCoordinates(width, height, (x, y) => {
  getNeighborCoordinates(width, height, x, y)
    .forEach(([neighborX, neighborY]) => {
      this.grid[x][y].neighbors.add(this._grid[neighborX][neighborY]);
    });
});

建议实现相同目标的替代方法很好,但我真的更感兴趣的是学习 TypeScript,而不是修复此代码以达到最佳状态。(昨晚我花了几个小时创建了一个 Boggle/Wordament 求解器——它工作得很好,而且构建起来很有趣。)

标签: node.jstypescriptgenerics

解决方案


我遇到了一些烦人的困难,就像你一样。我发现有用的一种类型函数是:

type ArrayUnlessVoid<T> = T extends void ? void : Array<T>;

它是一个条件类型,会T变成T[]unless Tis void,在这种情况下它就是void。我们可能想要这个,因为它统一了做什么map()forEach()做什么:它们每个都接受一个返回 aT和返回a 的回调ArrayUnlessVoid<T>

好的,这是输入verbGrid()

const verbGrid =
    <R, O>(arrayMethod: (
        callbackfn: (value: any, index: number, array: any[]) => R,
        thisArg?: any
    ) => O) =>
        (width: number, height: number, callback: ((x: number, y: number) => R)) => {

            const outerArrayMethod = arrayMethod as any as (
                callbackfn: (value: any, index: number, array: any[]) => O,
                thisArg?: any
            ) => ArrayUnlessVoid<O>;

            const result = outerArrayMethod.call(
                Array.from(Array(width).keys()),
                (x: number) => arrayMethod.call(
                    Array.from(Array(height).keys()),
                    (y: number) => callback(x, y)
                ));

            return result;
        }

布莱奇。如果可以采用将回调返回 -转换为 an并返回 an 的verbGrid东西,那就太好了。但是编译器并不能真正看到与前一个定义相匹配的内容。所以我添加了一个新的类型参数来代替并返回。TArrayUnlessVoid<T>ArrayUnlessVoid<ArrayUnlessVoid<T>>Array.prototype.map()OArrayUnlessVoid<T>ArrayUnlessVoid<O>

如果有一些更简单的方法来描述您arrayMethod在实现中使用的两种不同的上下文,那也很好。相反,我只是放弃并使用类型断言将外部调用提供arrayMethod给不同的类型签名。

无论如何,您可以看到这些工作如宣传的那样:

const createGrid = verbGrid(Array.prototype.map); 
// const createGrid: <U>(
//   width: number, height: number, callback: (x: number, y: number) => U
// ) => U[][]

const visitGridCoordinates = verbGrid(Array.prototype.forEach); 
// const visitGridCoordinates: (
//   width: number, height: number, 
//   callback: (x: number, y: number) => void
// ) => void

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

Playground 代码链接


推荐阅读