【问题标题】:Typescript: Cannot export a module that is a generic interface and contains other generic interfaces打字稿:无法导出作为通用接口并包含其他通用接口的模块
【发布时间】:2014-01-09 13:31:30
【问题描述】:

我正在尝试为 Bluebird 编写一个 CommonJS 声明文件,这是一个直接导出通用 Promise 类的 Promise 库。但是,该库还将其他几个泛型类导出为静态成员 (PromiseInspection),并且似乎无法使用 typescript 对其进行建模。

编辑:使用示例,说明模块的导出类如何工作:

import Promise = require('bluebird');
var promise:Promise<number> = Promise.cast(5);
var x:Promise.PromiseInspection<number> = promise.inspect();

我尝试了几种策略 - 简化示例如下:

1。显而易见的方式

declare module "bluebird" {
    class PromiseInspection<T> {
        // ...
    }
    class Promise<T> {
        PromiseInspection: typeof PromiseInspection; // error
        constructor<T>();
        inspect():PromiseInspection<T>; // error
        static cast<U>(value:U):Promise<U>; 
        // ...
    }
    export = Promise;
}

失败,错误无法使用私有类型PromiseInspection 作为公共属性

2。使用静态接口

declare module "bluebird2" {
    interface PromiseInspection<T> {
        // ...  
    }
    interface Promise<T> {
        constructor<T>();
        inspect():PromiseInspection<T>;
    }
    interface PromiseStatic {
        new<T>();
        PromiseInspection:typeof PromiseInspection;
        cast<U>(value:U):Promise<U>; // error
    }
    export = PromiseStatic;
}

同样失败,但这次私有类型是 Promise

3。尝试直接从模块中导出构造函数

declare module "bluebird3" {
    export interface PromiseInspection<T> {
        // ...
    }
    export interface Promise<T> {
        constructor<T>();
        inspect():PromiseInspection<T>;
    }

    export new<T>(); // syntax error
    export function cast<U>(value:U):Promise<U>; 
}

这几乎可以工作,当然构造函数不可能那样做。

4。命名空间污染方式(有效,有缺点)

interface PromiseInspection<T> {
    // ...
}
interface Promise<T> {
    constructor<T>();
    inspect():PromiseInspection<T>;
}

declare module "bluebird4" {    
    interface PromiseStatic {
        new<T>():Promise<T>;
        PromiseInspection: typeof PromiseInspection;
        cast<U>(value:U):Promise<U>;
    }
    export = PromiseStatic;
}

有效,但它使用 Promise 和 PromiseInspection 污染了全局命名空间。这可能没问题,但我宁愿避免它,因为在 CommonJS 中它通常被认为是不可接受的。

5。声明合并(让我完成了 90% 的工作......)

declare module "bluebird5" {
    module Promise {
        export interface PromiseInspection<T> {
            value(): T;
            // ...
        }
        export
        function cast<U>(value: U): Promise<U> ;
    }

    class Promise<T> {
        new <T> (): Promise <T> ;
        inspect(): Promise.PromiseInspection <T> ;
    }

    export = Promise;
}

差不多了 - 除了现在我不能用interface Promise&lt;T&gt; 替换class Promise&lt;T&gt;,这使得Promise&lt;T&gt; 无法扩展。如果我尝试这样做,以下代码:

import Promise = require('bluebird');
var x = new Promise<number>();
x.inspect().value().toExponential();

失败并出现错误“无效的'new'表达式”

Link to the actual, work-in-progress bluebird.d.ts - 这个当前污染了全局命名空间(使用解决方案 4)

有没有更好的方法来做到这一点,还是我遇到了语言限制?

【问题讨论】:

    标签: class generics module export typescript


    【解决方案1】:

    Anders Hejlsberg 在 CodePlex 上发布了一个答案,所以我将在此处添加它。声明合并解决方案很接近 - 但我还需要一个“var”声明来声明静态接口,因为它是唯一可以接受构造函数的接口。

    declare module "bluebird" {
        module Promise {
            export interface PromiseInspection<T> {
                value(): T;
            }
        }
        interface Promise<T> {
            inspect(): Promise.PromiseInspection <T> ;
        }
    
        var Promise: {
            new<U>(): Promise<U>;
            cast<U>(value: U): Promise<U> ;
        }
        export = Promise;
    }
    

    所以基本上:

    • 模块声明中的接口成员(只要它们仅声明类型,即非物理)
    • 主界面中的实例成员
    • var 声明中的静态函数成员、构造函数和其他“物理”成员。

    另外,他的评论:

    以这种方式编写它,您对标识符 Promise 的三个含义中的每一个都有一个单独的声明:作为命名空间(仅包含类型的模块)、作为类型(恰好是泛型)和作为值。

    【讨论】:

    • 嗨 Gorgi,BB 定义文件的状态如何?我没有看到有关 distinctlyTyped 的拉取请求,我想使用它。
    • @SeanClarkHess 看起来有人打败了我。见github.com/spion/DefinitelyTyped/blob/master/bluebird/…
    • 我的版本在 node 中效果更好,可以在这里找到 - github.com/spion/DefinitelyTyped/tree/master/bluebird
    • 太棒了。你已经为它提出了拉取请求吗?如果不是,你应该!谢谢!
    • @SeanClarkHess - 我遇到了 ts 0.9.5 和 node 的问题,其中仅使用返回承诺的东西需要导入承诺库。我决定暂时放弃 typescript,直到微软决定齐心协力解决他们在最新版本中引入的问题。
    【解决方案2】:

    查看您的代码,我注意到您缺少一些 export 语句。下面的代码可以编译 - 它适合吗?

    declare module bluebird {
        export class PromiseInspection<T> {
            // ...
        }
        export class Promise<T> {
            constructor<T>();
            inspect():PromiseInspection<T>;
            static all<T>(promises:Promise<T>[]):Promise<T[]>;
        }
    }
    
    declare module "bluebird" {
        export = bluebird;
    }
    

    虽然我通常喜欢在定义类型时使用接口,如 #2:

    declare module bluebird {
        export interface PromiseInspection<T> {
            // ...
        }
        export interface Promise<T> {
            constructor<T>();
            inspect():PromiseInspection<T>;
        }
        export interface PromiseStatic {
            new<T>();
            all<T>(promises:Promise<T>[]):Promise<T[]>;
        }
    }
    
    declare module "bluebird" {
        export = bluebird;
    }
    

    您是否尝试过使用另一个 Promise 库作为输入的基础?你可以做的比看https://github.com/borisyankov/DefinitelyTyped/blob/master/q/Q.d.ts更糟糕

    粗略地说,它们看起来有点像这样:

    declare function Q<T>(promise: Q.IPromise<T>): Q.Promise<T>;
    declare function Q<T>(promise: JQueryPromise<T>): Q.Promise<T>;
    declare function Q<T>(value: T): Q.Promise<T>;
    
    declare module Q {
        //… functions etc in here
    }
    
    declare module "q" {
        export = Q;
    }
    

    【讨论】:

    • 是的,我确实使用 Q 作为基础,但是 Q 将其承诺类导出为 Q.Promise,而 Bluebird 导出类构造函数本身......编辑:我添加了一个指向实际蓝鸟的链接。 d.ts
    • 关于缺少的导出语句 - Bluebird 不是包含“Promise”类的模块。模块 是 Promise 类,但也有一个静态成员 PromiseInspection,它本身就是一个泛型类。我将添加一个使用示例来说明
    • 添加了用法示例,说明我为什么使用export = Interface 或export = Class
    猜你喜欢
    • 2017-04-15
    • 2017-11-08
    • 2017-08-31
    • 1970-01-01
    • 1970-01-01
    • 2021-02-06
    • 2018-01-06
    • 2018-01-10
    • 1970-01-01
    相关资源
    最近更新 更多