node.js - 在打字稿中接受调用者的哪个数组方法
问题描述
背景:
以下功能之间的唯一区别是:
createGrid
使用map
(采用回调返回类型T
)并返回T[][]
visitGridCoordinates
使用forEach
(采用回调返回类型void
)并返回void
。
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
。当我尝试将联合类型用于整个类型描述符或仅用于返回值时,会发生各种不同的错误。
我尝试使用varof
as invarof Array<number>["map"] | varof Array<number>["forEach"]
来更明确地使用类型,并避免重新描述这两种方法类型。但是我仍然没有运气(我注意到map
版本没有指定U
任何地方,我不知道该怎么做)。
这甚至可能吗?如果是这样,你介意帮助我了解我哪里出错了吗?
我得到的各种结果:
verbGrid
成为 type的结果unknown
。- 没有错误,
verbGrid
但无法将结果分配给U[][]
调用站点的类型变量。 - 传入
'map'
和'forEach'
作为字符串并将它们用作[method]
(而不是做method.call
)。
我试图简化一些事情,如下所示:
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 求解器——它工作得很好,而且构建起来很有趣。)
解决方案
我遇到了一些烦人的困难,就像你一样。我发现有用的一种类型函数是:
type ArrayUnlessVoid<T> = T extends void ? void : Array<T>;
它是一个条件类型,会T
变成T[]
unless T
is 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
东西,那就太好了。但是编译器并不能真正看到与前一个定义相匹配的内容。所以我添加了一个新的类型参数来代替并返回。T
ArrayUnlessVoid<T>
ArrayUnlessVoid<ArrayUnlessVoid<T>>
Array.prototype.map()
O
ArrayUnlessVoid<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
好的,希望有帮助;祝你好运!
推荐阅读
- f# - 如何在不覆盖现有运算符的情况下定义运算符重载
- python - 如果语句没有被评估?Python
- javascript - 用 babel 编译代码后,属性不存在
- android - 如何使用ffmpeg在视频图像之间添加过渡效果
- angular - $scope.$apply(); 的等价物是什么?在 Angular 打字稿中?
- python - 防止 msg 与 asyncio 套接字连接
- javascript - 具有许多子模块的打字稿项目
- bash - Bash 使用分隔符“,”拆分字符串并在字符串中保留空格
- javascript - 我如何在函数内部的函数中获取返回值?
- c++ - 可变参数模板模板的完美转发