英文:
Rest parameters when overloading with optional
问题
在重载函数时,有时我会使用以下模式:
export function foo(a: string): void;
export function foo(b: number): void;
export function foo(a: string, b: number): void;
export function foo(...args: [string] | [number] | [string, number]): void {
/* ... */
}
这样可以轻松确定使用哪个原型,例如,如果 args.length === 2
,则已知 args
是 [string, number]
。对于更复杂的类型(考虑如果你有两个接口而不是 string
和 number
),这可以简化很多事情。
但是,在使用可选参数时,我无法使其工作:
export function foo(a?: string): void;
// ~~~ error TS2394: This overload signature is not compatible with its implementation signature.
export function foo(...args: [] | [string]): void {
return;
}
我知道可以简单地添加另一个不带可选参数的重载,但组合将迅速变得难以管理。
export function foo(): void;
export function foo(a: string): void;
export function foo(...args: [] | [string]): void {
return;
}
更具体的示例如下:
interface Foo {
/* ... */
}
interface Bar {
/* ... */
}
interface Baz {
/* ... */
}
function awesomeFunction(foo?: Foo, bar?: Bar): void;
function awesomeFunction(baz: Baz, foo?: Foo, bar?: Bar): void;
function awesomeFunction(...args:
| [Foo | undefined, Bar | undefined]
| [Baz, Foo | undefined, Bar | undefined]
): void {
/* ... */
}
由于我知道如果 args[0]
是 Baz
,我也会知道 args
中的其他元素。但是,如上所示,它不适用于 foo
函数。
逐个列出所有排列组合将会产生大量的变体,而逐个参数输入为 arg1?: Foo | Baz, arg2: Foo | Bar, arg3: Bar
意味着我必须对不可能的变体进行类型检查。
有没有方法解决这个问题?
英文:
When overloading functions I sometimes use the following pattern:
export function foo(a: string): void;
export function foo(b: number): void;
export function foo(a: string, b: number): void;
export function foo(...args: [string] | [number] | [string, number]): void {
/* ... */
}
This makes it easy to determine which of the prototypes is used, e.g. if args.length === 2
then args
is known to be [string, number]
. With more complex types (consider if instead of string
and number
you have two interfaces) this can simplify a great deal.
However, I cannot get this working when using optional parameters:
export function foo(a?: string): void;
// ~~~ error TS2394: This overload signature is not compatible with its implementation signature.
export function foo(...args: [] | [string]): void {
return;
}
I know I can simply add another overload without the optional parameter but the combinations would quickly become unmanageable.
export function foo(): void;
export function foo(a: string): void;
export function foo(...args: [] | [string]): void {
return;
}
A more concrete example of this would be:
interface Foo {
/* ... */
}
interface Bar {
/* ... */
}
interface Baz {
/* ... */
}
function awesomeFunction(foo?: Foo, bar?: Bar): void;
function awesomeFunction(baz: Baz, foo?: Foo, bar?: Bar): void;
function awesomeFunction(...args:
| [Foo | undefined, Bar | undefined]
| [Baz, Foo | undefined, Bar | undefined]
): void {
/* ... */
}
Since I know that if args[0]
is a Baz
I would know the rest of the elements in args
as well. It does however not work as seen above with the foo
function.
Typing out all the permutations would become a lot of variants and typing out each parameter as arg1?: Foo | Baz, arg2: Foo | Bar, arg3: Bar
means I have to typecheck for impossible variants.
Is there a way to work around this issue?
答案1
得分: 2
这里有一个函数参数列表和元组类型之间的直接等价关系。特别是,像(x?: string) => void
中的可选函数参数对应于像[string?]
(或者当你使用元组元素标签时,[x?: string]
)中的可选元组元素。
这意味着你可以像这样编写你的重载 实现:
function foo(a?: string): void;
function foo(...args: [string?]): void {
}
function awesomeFunction(foo?: Foo, bar?: Bar): void;
function awesomeFunction(baz: Baz, foo?: Foo, bar?: Bar): void;
function awesomeFunction(...args: [Foo?, Bar?] | [Baz, Foo?, Bar?]): void {
}
实际上,如果所有调用签名的返回类型都相同,你甚至不需要重载:
function bar(...args: [a?: string]): void { }
// bar(a?: string | undefined): void
当你调用接受剩余元组联合的函数时,它看起来像一个重载函数:
function awesomeFunction(...args:
[foo?: Foo, bar?: Bar] | [baz: Baz, foo?: Foo, bar?: Bar]
): void { }
// 1/2 awesomeFunction(foo?: Foo | undefined, bar?: Bar | undefined): void
// 2/2 awesomeFunction(baz: Baz, foo?: Foo | undefined, bar?: Bar | undefined): void
虽然[string?]
和[string] | []
之间可能存在关系,但当可选元组元素可用时,强迫编译器看到这一点可能没有意义。
英文:
There's essentially a direct equivalence between function parameter lists and tuple types. In particular, optional function parameters like in (x?: string) => void
correspond to optional tuple elements like [string?]
(or, when you use tuple element labels, [x?: string]
).
That means you could write your overload implementations like this:
function foo(a?: string): void;
function foo(...args: [string?]): void {
}
function awesomeFunction(foo?: Foo, bar?: Bar): void;
function awesomeFunction(baz: Baz, foo?: Foo, bar?: Bar): void;
function awesomeFunction(...args: [Foo?, Bar?] | [Baz, Foo?, Bar?]): void {
}
Indeed if the return types are identical for all the call signatures, you don't even need overloads:
function bar(...args: [a?: string]): void { }
// bar(a?: string | undefined): void
and a function accepting a union of rest tuples looks like an overloaded function when you call it:
function awesomeFunction(...args:
[foo?: Foo, bar?: Bar] | [baz: Baz, foo?: Foo, bar?: Bar]
): void { }
// 1/2 awesomeFunction(foo?: Foo | undefined, bar?: Bar | undefined): void
// 2/2 awesomeFunction(baz: Baz, foo?: Foo | undefined, bar?: Bar | undefined): void
And while there are arguably a relationship between [string?]
and [string] | []
, there's probably no point in trying to force the compiler to see it when optional tuple elements are available.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论