具有特定数字的函数重载
Function overload with specific number
想象一下我的要求的简化版本:我将一个项目数组传递给一个函数、一个回调以及我想从数组中弹出的项目数。回调将获得该数量的项目。
如果弹出计数设置为 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
的默认值是 1
,我通过在 count
的类型是 1
.)
请注意 TypeScript 会假定任何非文字(或至少不是立即明显不变的)number
你作为 count
传递意味着回调需要一个数组,因为类型是 number
,而不是 1
。因此,如果您在调用中指定 1
literally(或作为一个立即明显的常量),则此重载仅用于为项目(而不是数组)提供回调.
示例:
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
的类型不是 number[]
,在运行时它也会收到一个 number
,而不是一个数组,因为运行时代码只知道 [=14] =]参数是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[]
的情况变成了编译时错误:playground link
想象一下我的要求的简化版本:我将一个项目数组传递给一个函数、一个回调以及我想从数组中弹出的项目数。回调将获得该数量的项目。
如果弹出计数设置为 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
的默认值是 1
,我通过在 count
的类型是 1
.)
请注意 TypeScript 会假定任何非文字(或至少不是立即明显不变的)number
你作为 count
传递意味着回调需要一个数组,因为类型是 number
,而不是 1
。因此,如果您在调用中指定 1
literally(或作为一个立即明显的常量),则此重载仅用于为项目(而不是数组)提供回调.
示例:
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
的类型不是 number[]
,在运行时它也会收到一个 number
,而不是一个数组,因为运行时代码只知道 [=14] =]参数是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[]
的情况变成了编译时错误:playground link