typescript - 具有特定编号的函数重载
问题描述
想象一下我的要求的简化版本:我将一个项目数组传递给一个函数、一个回调以及我想从数组中弹出多少个项目。回调将获得该数量的项目。
如果弹出计数设置为1
,我希望该回调仅接收该单个项目。如果它是来自的其他任何东西1
,我希望它将一个数组传递给回调。
我不确定在 TypeScript 中是否可行。我一直在玩,但没有成功。这是我想出的(但不起作用):
function pop<T>(items: T[], cb: (item: T) => void, count: 1): void;
function pop<T>(items: T[], cb: (item: T[]) => void, count: undefined): void;
function pop<T>(items: T[], cb: (item: T[]) => void, count: number): void;
function pop<T>(
items: T[],
cb: ((item: T) => void) | ((item: T[]) => void),
count = 1,
): void {
if (count === 1) {
cb(items[0]);
} else if (count > 1) {
cb(items.slice(0, count));
} else {
cb([]);
}
}
谁能告诉我这是否可能?或者我是否遗漏了什么?
解决方案
有可能,您只需要放松实现签名(调用者看不到,他们只看到重载签名):
function pop<T>(items: T[], cb: (item: T) => void, count?: 1): void;
function pop<T>(items: T[], cb: (items: T[]) => void, count: number): void;
function pop<T>(
items: T[],
cb: (items: T | T[]) => void,
count = 1,
): void {
if (count === 1) {
cb(items[0]);
} else if (count > 1) {
cb(items.slice(0, count));
} else {
cb([]);
}
}
(由于默认为count
is 1
,因此我将其折叠到第一个重载签名中,count
方法是在其类型为 时设置为可选1
。)
请注意,尽管TypeScript 会假定您传递的任何非文字(或至少不是立即明显的常量)都表示回调需要一个数组,因为类型是,而不是。因此,如果您在调用中按字面意思指定(或作为立即明显的常量),则此重载仅适用于为回调提供项目(而不是数组) 。number
count
number
1
1
例子:
declare let items: number[];
pop(
items,
(item) => {
console.log(item); // type is `number`
},
1
);
let count = 1;
pop(
items,
(item) => {
console.log(item); // type is `number[]`, not `number`
},
count
);
即使不是item
is的类型number[]
,在运行时它也会接收单个number
,而不是数组,因为运行时代码只知道count
参数是1
,而不是为什么它是1
。正如 Jörg W Mittag 在评论中指出的那样,这是因为重载纯粹是 TypeScript 中的编译时/类型检查。在运行时实际发生的唯一部分是 JavaScript 实现,它不知道静态类型。(这与像 Java 这样的语言形成对比,其中重载实际上是单独的函数,并且被调用的特定函数是在编译时确定的,而不是运行时。)
您可以通过以下几种方式解决此问题:
- 而是定义两个单独的方法,
popOne
和pop
/popSome
或类似的方法。 - 要求
const
仅是文字或编译时常量。
#1 是不言自明的,但是 Captain-yossarian 通过OnlyLiteral
泛型向我们展示了如何做 #2:
type OnlyLiteral<N> = N extends number ? number extends N ? never : N : never;
那么第二个重载签名是:
function pop<T>(items: T[], cb: (items: T[]) => void, count: OnlyLiteral<number>): void;
...number[]
在我之前的示例中给我们的情况变成了编译时错误:操场链接
推荐阅读
- mysql - 查询 Laravel 选择 Where 数组
- visual-studio-code - 如何从 vs 代码扩展 api 显示是/否对话框
- xml - 为自定义模块创建基于类别的 Qweb 报告
- r - ELO 评级算法中的 for 循环错误
- python - 如何在不使用 replace() 函数的情况下将字符串中的子字符串转换为大写?
- r - 可以只为ggplot2中的某些geom关闭裁剪吗?
- javascript - 带有属性的 Javascript for 循环
- mongodb - 带有 kafka 和 mongoDB 连接器的发件箱模式
- python - Python 多次打印相同的数据
- javascript - 在输入值更改时更新其他组件 React