我正在为具有全局结构的 JavaScript 游戏引擎编写 TypeScript 声明文件,我不太知道如何处理这个返回类构造函数的函数。这是代码的简化版本:
var createScript = function(name) {
var script = function(args) {
this.app = args.app;
this.entity = args.entity;
}
script._name = name
return script;
}
用户打算使用自己的代码扩展
script
类构造函数,如下所示:var MyScript = createScript('myScript');
MyScript.someVar = 6;
MyScript.prototype.update = function(dt) {
// Game programming stuff
}
在我的声明文件中处理
createScript
函数和 script
类构造函数的正确方法是什么?更新
我已经更新了我的代码示例,以表明用户还需要扩展“类”的静态方面。到目前为止给出的答案虽然很棒,但似乎不允许这样做。
最佳答案
在 TypeScript 中,表示类构造函数的一种可能方法是使用带有所谓 constructor signature 的接口(interface)。因为 createScript
应该返回用户创建(或更准确地说,用户修改)类的实例,所以它必须是通用的。用户必须提供一个接口(interface),将扩展类描述为 createScript
的通用参数:
export interface ScriptConstructorArgs {
app: {}; // dummy empty type declarations here
entity: {};
}
export interface Script { // decsribes base class
app: {};
entity: {};
}
export interface ScriptConstructor<S extends Script> {
_name: string;
new(args: ScriptConstructorArgs): S;
}
// type declaration for createScript
declare var createScript: <S extends Script>(name: string) => ScriptConstructor<S>;
使用
createScript
时,用户必须描述扩展类并通过分配给原型(prototype)单独提供其实现interface MyScript extends Script {
update(dt: {}): void;
}
var MyScript = createScript<MyScript>('myScript');
MyScript.prototype.update = function(dt) {
// Game programming stuff
}
更新
如果您希望用户也能够扩展构造函数类型(以自定义类的“静态”端),您可以通过一些额外的工作来实现。它涉及为自定义构造函数类型添加另一个泛型参数。用户还必须提供描述该类型的接口(interface) - 在此示例中为
MyScriptClass
:export interface ScriptConstructorArgs {
app: {}; // dummy empty type declarations here
entity: {};
}
export interface Script { // decsribes base class
app: {};
entity: {};
}
export interface ScriptConstructor<S extends Script> {
_name: string;
new(args: ScriptConstructorArgs): S;
}
// type declaration for createScript
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) {
// Game programming stuff
}
MyScript.someVar = 6;
请注意,在此解决方案中,编译器并未真正检查
提供的实现符合声明的接口(interface) - 您可以将任何内容分配给
prototype.update
并且编译器不会提示。此外,在分配 prototype.update
和 someVar
之前,当您尝试使用它们时,您会得到 undefined ,并且编译器也不会捕捉到它。另一个更新
对
prototype.udpate
的赋值没有进行类型检查的原因是 MyScript
的静态部分被推断为 Function
,并且 Function.prototype
在 built-in library 中被声明为 any
,从而抑制了类型检查。有一个简单的解决方法:只需声明您自己的更具体的 prototype
:export interface ScriptConstructor<S extends Script> {
_name: string;
new(args: ScriptConstructorArgs): S;
prototype: S;
}
然后它开始捕捉这样的错误:
MyScript.prototype.udpate = function(dt) {
// Game programming stuff
}
// Property 'udpate' does not exist on type 'MyScript'. Did you mean 'update'?
此外,
dt
参数类型也是从 MyScript
接口(interface)推断出来的。实际上我没有太多做这样的事情的经验,所以我不知道声明自己的原型(prototype)是否会导致将来其他代码出现一些问题。
此外,如果对
prototype.update
的赋值完全丢失,它也不会提示——它仍然会相信 update
存在,因为它是在 createScript
类型中声明的。这就是从 javascript 实现中“组装”一个类以及一些外部类型声明的代价——创建类的“正常”、全 typescript 方式只是使用类似 class MyScript extends ScriptBase implements SomeInterface
的东西,然后保证会被类型检查。关于javascript - 返回类构造函数的函数的类型声明,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47933255/