在TypeScript中,表示类构造函数的一种可能方式是使用带有所谓“
构造函数签名”的接口。因为
createScript
应该返回用户创建(或更准确地说,用户修改)类的实例,所以它必须是泛型的。用户将不得不提供一个描述扩展类的接口作为
createScript
的泛型参数:
export interface ScriptConstructorArgs {
app: {};
entity: {};
}
export interface Script {
app: {};
entity: {};
}
export interface ScriptConstructor<S extends Script> {
_name: string;
new(args: ScriptConstructorArgs): S;
}
declare var createScript: <S extends Script>(name: string) => ScriptConstructor<S>;
使用
createScript
时,用户必须描述扩展类并将其实现分别分配给原型。
interface MyScript extends Script {
update(dt: {}): void;
}
var MyScript = createScript<MyScript>('myScript');
MyScript.prototype.update = function(dt) {
}
更新:
如果您希望用户也能够扩展构造函数类型(以自定义类的“静态”部分),那么您可以通过一些额外的工作来实现。这涉及添加另一个泛型参数以用于自定义构造函数类型。用户还必须提供描述该类型的接口 - 在此示例中为
MyScriptClass
。
export interface ScriptConstructorArgs {
app: {};
entity: {};
}
export interface Script {
app: {};
entity: {};
}
export interface ScriptConstructor<S extends Script> {
_name: string;
new(args: ScriptConstructorArgs): S;
}
declare var createScript: <Instance extends Script,
Class extends ScriptConstructor<Instance>
>(name: string) => Class;
interface MyScript extends Script {
update(dt: {}): void;
}
interface MyScriptClass extends ScriptConstructor<MyScript> {
someVar: number;
}
var MyScript = createScript<MyScript, MyScriptClass>('myScript');
MyScript.prototype.update = function(dt) {
}
MyScript.someVar = 6;
请注意,在这个解决方案中,编译器并没有真正检查提供的实现是否符合声明的接口 - 你可以将任何东西分配给
prototype.update
,编译器不会抱怨。此外,在分配
prototype.update
和
someVar
之前,当您尝试使用它们时,您会得到未定义的结果,并且编译器也不会捕获这一点。
另一个更新:
prototype.udpate
的赋值不进行类型检查的原因是,某种方式推断出
MyScript
的静态部分是一个
Function
,而
Function.prototype
在
内置库中被声明为
any
,这会抑制类型检查。有一个简单的解决方法:只需声明自己更具体的
prototype
即可。
export interface ScriptConstructor<S extends Script> {
_name: string;
new(args: ScriptConstructorArgs): S;
prototype: S;
}
然后它开始捕捉这样的错误:
MyScript.prototype.udpate = function(dt) {
}
此外,
dt
参数类型也是从
MyScript
接口推断出来的。
实际上,我没有太多像这样做事情的经验,所以我不知道声明自己的原型是否会在将来与其他代码造成问题。
另外,如果完全省略对
prototype.update
进行赋值,它也不会抱怨——因为它在
createScript
类型中被声明,它依然相信
update
存在。这是从JavaScript实现中“组装”一个类与一些外部类型声明的代价——“正常”的、全TypeScript创建类的方式只需使用类似于
class MyScript extends ScriptBase implements SomeInterface
的语法,这样就可以保证类型检查。
name
属性是只读的。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/name - Robert Pennereval
或new Function
。https://dev59.com/lm025IYBdhLWcg3wnXSf - Robert Penner