簡體   English   中英

返回類構造函數的函數的類型聲明

[英]Type declaration for function that returns a class constructor

我正在為具有全局結構的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類構造函數的正確方法是什么?

更新我已經更新了我的代碼示例,以表明用戶也應該擴展“類”的靜態方面。 到目前為止給出的答案雖然很棒,但似乎並不允許這樣做。

您可以將createScript的返回類型聲明為某種通用腳本構造函數

interface AbstractScript { 
    app;
    entity;
}

interface ScriptConstructor<T extends AbstractScript> { 
    new(args: AbstractScript): T;
    readonly prototype: T;
}

declare function createScript(name: string): ScriptConstructor<any>;

然后

interface Foo extends AbstractScript { 
    foo(): void;
}

let Foo:ScriptConstructor<Foo> = createScript("Foo");
Foo.prototype.foo = function () { }

let d = new Foo({app: "foo", entity: "bar"});
d.foo();
d.entity
d.app

注意:出於方便原因,將構造函數和創建的類的接口命名為Foo
首先,TS可能有一個問題,但不是,它很好地區分。

在TypeScript中,表示類構造函數的一種可能方法是使用具有所謂構造函數簽名的接口。 因為createScript應該返回用戶創建的(或者更確切地說是用戶修改的)類的實例,所以它必須是通用的。 用戶必須提供一個接口,將擴展類描述為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 ,用戶必須描述擴展類,並通過分配原型單獨提供其實現

interface MyScript extends Script {
    update(dt: {}): void;
}
var MyScript = createScript<MyScript>('myScript');

MyScript.prototype.update = function(dt) {
  // Game programming stuff
}

UPDATE

如果您希望用戶也能夠擴展構造函數類型(以自定義類的“靜態”方面),您可以通過一些額外的工作來完成。 它涉及為自定義構造函數類型添加另一個泛型參數。 用戶還必須提供描述該類型的接口 - 在此示例中為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;

請注意,在此解決方案中,編譯器並未真正檢查提供的實現是否符合聲明的接口 - 您可以為prototype.update分配任何內容,編譯器不會抱怨。 此外,在分配prototype.updatesomeVar之前,當您嘗試使用它們時,您將獲得未定義,並且編譯器也不會捕獲它。

另一個更新

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) {
  // Game programming stuff
}
// Property 'udpate' does not exist on type 'MyScript'. Did you mean 'update'?

此外,還從MyScript界面推斷出dt參數類型。

實際上我沒有太多經驗做這樣的事情,所以我不知道聲明你自己的原型是否會在將來導致其他代碼出現問題。

此外,如果對prototype.update的賦值完全丟失,它也不會抱怨 - 它仍然會認為update是因為它是以createScript類型聲明的。 這是從javascript實現中“組裝”一個類以及一些外部類型聲明所付出的代價 - 創建類的“普通”,all-typescript方法只是使用class MyScript extends ScriptBase implements SomeInterface ,然后它保證被挑剔了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM