英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论