如何防止使用错误的参数类型调用重载函数
How to prevent an overloaded function from being called with the wrong argument types
我想为一些 JS 代码生成 typescript 定义,这有点尴尬,因为有些 类 的静态方法与其父类的静态方法同名但类型签名不同。更多详情 here and here.
我的解决方案尝试是使用像这样的通用重载:
export class Parent {
...
static new(arg1: string): Parent
}
export class Child extends Parent {
...
static new(arg1: number, arg2: number): Child
static new<T, V>(arg1: T): V
}
这可能会被 T 的任意类型调用泛型形式所滥用,从而导致未定义的行为。有没有办法防止这种情况发生? Typescript 坚持所有具有相同名称的重载具有相同的可见性,所以我不能将通用的重载设为私有。但是,如果没有办法限制对具有固定类型的签名之一的调用,那么我们似乎已经错过了重载的最佳好处之一。
无论您如何处理重载签名,TypeScript 已决定静态方法将从父 classes (as possibly implied by ES2015) 继承,因此您不能只扩展 class 并解决这个问题。至少直到并且除非 TS 发生变化。例如:
namespace Original {
declare namespace Classes {
class Parent {
static method(arg1: string): Parent;
parentProp: string;
}
class Child extends Parent {
static method(arg1: number, arg2: number): Child;
static method<T, V>(arg1: T): V;
childProp: string;
}
}
const child = new Classes.Child();
const parent = new Classes.Parent();
const alsoParent: typeof parent = new Classes.Child();
const childConstructorMethod = Classes.Child.method;
const parentConstructorMethod = Classes.Parent.method;
// the following inheritance should hold
const alsoParentConstructorMethod: typeof parentConstructorMethod = childConstructorMethod;
// which leads to this even without generics
alsoParentConstructorMethod("oopsie-daisy");
}
这与您的 classes 类似,您可以看到,无论您做什么,上面的代码最终都会让人们在子构造函数上调用 parent-signature 方法...因为可以将子静态方法分配给父静态方法类型的变量。这就是继承,它对您无法使用签名解决的事情施加了限制。
那你能做什么?我会考虑更改声明的类型,以便根本不使用 class
。请注意,这并不意味着 JS 不能使用 class
;这只是意味着表示这种关系的最佳方式是使用您自己的类型和值。例如:
namespace PossibleFix {
declare namespace Classes {
interface Parent {
parentProp: string;
}
export const Parent: {
method(arg1: string): Parent;
new (): Parent;
};
interface Child extends Parent {
childProp: string;
}
export const Child: {
method(arg1: number, arg2: number): Child;
new (): Child;
};
}
const child = new Classes.Child();
const parent = new Classes.Parent();
const alsoParent: typeof parent = new Classes.Child();
const childConstructorMethod = Classes.Child.method;
const parentConstructorMethod = Classes.Parent.method;
// now there is no implied inheritance on the static side
const alsoParentConstructorMethod: typeof parentConstructorMethod = childConstructorMethod;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ <-- error! not assignable
}
这与最初的基于 class
的类型几乎相同。 class
有一个实例端和一个静态端。在这里,实例端变成了一个interface
,静态端变成了一个导出的const
变量,它既是构造函数(new():...
)又具有方法。但是在这里,即使实例端继承(我们仍然有 Child extends Parent
),静态端现在彼此无关。这允许您在子构造函数的方法上选择您想要的任何签名。
这最终会变得更加冗长,特别是如果您有生成原始声明的实际 TypeScript 代码,因为现在您必须想出一种方法来断言生成的代码符合后面的声明,并且它可能很乱。但是,它给了你更多的自由。希望能有所帮助。祝你好运!
我想为一些 JS 代码生成 typescript 定义,这有点尴尬,因为有些 类 的静态方法与其父类的静态方法同名但类型签名不同。更多详情 here and here.
我的解决方案尝试是使用像这样的通用重载:
export class Parent {
...
static new(arg1: string): Parent
}
export class Child extends Parent {
...
static new(arg1: number, arg2: number): Child
static new<T, V>(arg1: T): V
}
这可能会被 T 的任意类型调用泛型形式所滥用,从而导致未定义的行为。有没有办法防止这种情况发生? Typescript 坚持所有具有相同名称的重载具有相同的可见性,所以我不能将通用的重载设为私有。但是,如果没有办法限制对具有固定类型的签名之一的调用,那么我们似乎已经错过了重载的最佳好处之一。
无论您如何处理重载签名,TypeScript 已决定静态方法将从父 classes (as possibly implied by ES2015) 继承,因此您不能只扩展 class 并解决这个问题。至少直到并且除非 TS 发生变化。例如:
namespace Original {
declare namespace Classes {
class Parent {
static method(arg1: string): Parent;
parentProp: string;
}
class Child extends Parent {
static method(arg1: number, arg2: number): Child;
static method<T, V>(arg1: T): V;
childProp: string;
}
}
const child = new Classes.Child();
const parent = new Classes.Parent();
const alsoParent: typeof parent = new Classes.Child();
const childConstructorMethod = Classes.Child.method;
const parentConstructorMethod = Classes.Parent.method;
// the following inheritance should hold
const alsoParentConstructorMethod: typeof parentConstructorMethod = childConstructorMethod;
// which leads to this even without generics
alsoParentConstructorMethod("oopsie-daisy");
}
这与您的 classes 类似,您可以看到,无论您做什么,上面的代码最终都会让人们在子构造函数上调用 parent-signature 方法...因为可以将子静态方法分配给父静态方法类型的变量。这就是继承,它对您无法使用签名解决的事情施加了限制。
那你能做什么?我会考虑更改声明的类型,以便根本不使用 class
。请注意,这并不意味着 JS 不能使用 class
;这只是意味着表示这种关系的最佳方式是使用您自己的类型和值。例如:
namespace PossibleFix {
declare namespace Classes {
interface Parent {
parentProp: string;
}
export const Parent: {
method(arg1: string): Parent;
new (): Parent;
};
interface Child extends Parent {
childProp: string;
}
export const Child: {
method(arg1: number, arg2: number): Child;
new (): Child;
};
}
const child = new Classes.Child();
const parent = new Classes.Parent();
const alsoParent: typeof parent = new Classes.Child();
const childConstructorMethod = Classes.Child.method;
const parentConstructorMethod = Classes.Parent.method;
// now there is no implied inheritance on the static side
const alsoParentConstructorMethod: typeof parentConstructorMethod = childConstructorMethod;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ <-- error! not assignable
}
这与最初的基于 class
的类型几乎相同。 class
有一个实例端和一个静态端。在这里,实例端变成了一个interface
,静态端变成了一个导出的const
变量,它既是构造函数(new():...
)又具有方法。但是在这里,即使实例端继承(我们仍然有 Child extends Parent
),静态端现在彼此无关。这允许您在子构造函数的方法上选择您想要的任何签名。
这最终会变得更加冗长,特别是如果您有生成原始声明的实际 TypeScript 代码,因为现在您必须想出一种方法来断言生成的代码符合后面的声明,并且它可能很乱。但是,它给了你更多的自由。希望能有所帮助。祝你好运!