TypeScript中所有重载参数类型的类型
考虑以下示例:TypeScript中所有重载参数类型的类型,typescript,Typescript,考虑以下示例: class A { private constructor(public n: number) {} public getDouble() { return this.n * 2; } static from(s: string): A; static from(n: number): A; static from(n1: number, n2: number): A; static from(...args: unknown[]): A {
class A {
private constructor(public n: number) {}
public getDouble() {
return this.n * 2;
}
static from(s: string): A;
static from(n: number): A;
static from(n1: number, n2: number): A;
static from(...args: unknown[]): A {
if (args.length === 1) {
if (typeof args[0] === 'string') {
if (args[0].length !== 1) {
throw new Error('String must have a length of 1')
}
return new A(Number(args[0]));
} else if (typeof args[0] === 'number') {
if (args[0] > 9) {
throw new Error('Number must be lower than 10')
}
return new A(args[0]);
}
} else if (args.length === 2) {
if (typeof args[0] === 'number' && typeof args[1] === 'number') {
const sum = args[0] + args[1];
if (sum > 9) {
throw new Error('Sum of numbers must be lower than 10')
}
return new A(sum);
}
}
throw new Error('No overload matched')
}
}
A
的实例只能使用来自的静态方法构造,该方法重载为获取字符串、数字或两个数字。有效参数仅包括长度为1的字符串、小于10的数字以及总和小于10的数字对-其他参数会导致错误
我想实现另一个静态方法validate
,它将检查A
的实例是否可以从给定的参数列表中构造,如下所示:
static validate(...args: Parameters<typeof A.from>): boolean {
try {
A.from(...args);
return true;
} catch (error) {
return false;
}
}
type MyOverload<ReturnType> = {
(s: string): ReturnType;
(n: number): ReturnType;
(n1: number, n2: number): ReturnType;
}
静态验证(…参数:参数):布尔值{
试一试{
A.来自(…args);
返回true;
}捕获(错误){
返回false;
}
}
问题是,Parameters
解析为列表中最后一个重载的参数,在这种情况下,n1:number,n2:number
,这会导致TypeScript不允许使用任何其他重载签名调用,即A.validate('foo')
我如何使TypeScript理解可以使用与来自
的相同的重载签名调用验证
我找到了两种可能的解决方案,但都有严重的缺点:
我可以手动构造另一个重载签名来匹配所有以前的重载,并将其放在列表的末尾。然而,这将很难维护,并且会干扰IntelliSense提示
与1类似,但签名将允许…args:unknown[]
。然而,这会使TypeScript认为,当不能使用任何参数时,实际上可以使用任何参数调用from
我可以从
重载签名中复制并粘贴所有,并将它们声明为验证
。同样,很难维护-实际用例涉及更多重载以及多个模拟来自
重载的静态方法,这将导致50多行重载声明
还有别的办法解决这个问题吗?有没有办法写一个重载参数类型?我找到了一个基于解决方案3的解决方法。我试图找出是否有一种方法可以声明共享同一重载列表的多个成员。这并不漂亮,但我不相信有更优雅的方式来处理这个问题
解决方案包括将类转换为类型化类表达式,并为重载编写泛型类型。继续问题中的示例,重载类型如下所示:
static validate(...args: Parameters<typeof A.from>): boolean {
try {
A.from(...args);
return true;
} catch (error) {
return false;
}
}
type MyOverload<ReturnType> = {
(s: string): ReturnType;
(n: number): ReturnType;
(n1: number, n2: number): ReturnType;
}
这将实现:
- 可以使用相同的重载参数列表声明多个方法,但返回类型不同
- 他们的呼叫签名将正确键入,并与IntelliSense配合使用
限制(据我所知):
- 您需要在接口中声明类的公共端,然后在类中重新声明和实现它,并使这两个声明保持同步。这对于重载方法来说显然是一个优势,但对于其他方法来说则不是这样,并且会给维护带来负担
- 没有受保护的成员,既不是实例也不是静态的(因为接口和类型不能声明私有/受保护的成员)
- 为重载方法提供额外的参数将很麻烦
您可以进一步泛化重载类型,以获取额外的参数或多个参数(例如,对于createAndMultiplyBy
)之类的方法:
类型MyOverload={
(s:string,arg:MethodArg):返回类型;
(n:number,arg:MethodArg):返回类型;
(n1:number,n2:number,arg:MethodArg):返回类型;
}
然后将方法声明为createAndMultiply:MyOverload
。但是,额外的参数不会得到名称,如果有多个(即类型MyOverload
),则IntelliSense必须将它们显示为MyOverload
-(s:string,…args:[数字,数字])
在我的用例中,这些额外的重载方法应该只是一个额外的糖分实用程序层。我认为维护这些额外接口的开销不值得,于是放弃了实现它们。如果您有一个重载函数/方法,其中每个调用签名都具有相同的返回类型(如from()
的情况,其中返回类型总是A
),您可以将函数重构为一个版本,其中包含一个rest参数,该参数的类型为:
这完全可以像以前一样调用:
A.from("okay"); // works
A.from(1); // okay
A.from(2, 4); // okay
A.from("oops", 2); // error!
事实上,从IntelliSense的角度来看,它甚至看起来像一个重载函数:
/* A.from(⎀) */
// 1/3 from(s: string): A
// 2/3 from(n: number): A
// 3/3 from(n1: number, n2: number): A
但至关重要的是,参数
现在是元组的完全并集,不会丢失任何信息:
static validate(...args: Parameters<typeof A.from>): boolean {
// same impl as before
}
/* A.from(⎀) */
// 1/3 from(s: string): A
// 2/3 from(n: number): A
// 3/3 from(n1: number, n2: number): A
static validate(...args: Parameters<typeof A.from>): boolean {
// same impl as before
}
A.validate("okay"); // works
A.validate(1); // okay
A.validate(2, 4); // okay
A.validate("oops", 2); // error!