【问题标题】:Exporting a TypeScript module results in interfaces not getting picked up导出 TypeScript 模块会导致接口不被拾取
【发布时间】:2012-11-14 10:25:38
【问题描述】:

我同时使用 TypeScript 和 SignalR,并尝试为生成的 SignalR 类定义静态类型。如果我做这样的事情,它会起作用:

///<reference path="../Scripts/jquery-1.8.d.ts" />
///<reference path="../Scripts/signalr-1.0.d.ts" />

interface SignalR {
    roomHub: Service.RoomHub;
}

module Service {
        export var roomHub = $.connection.roomHub;
        export interface RoomHub { }
}

当然$.connectionSignalR 类型,它在文件“signalr-1.0.d.ts”中定义,并在上面的文件中扩展。

但是,我需要能够从其他文件中引用Service模块,所以我需要在模块和接口中添加“export”关键字,即:

///<reference path="../Scripts/jquery-1.8.d.ts" />
///<reference path="../Scripts/signalr-1.0.d.ts" />

export interface SignalR {
    roomHub: Service.RoomHub;
}

export module Service {
    // Error here: "The property 'roomHub' does not exist on type SignalR."
    export var roomHub = $.connection.roomHub;
    export interface RoomHub { }
}

但是,当我这样做时,$.connection.roomHub 下会出现一条红色的波浪线,并且编译器会返回错误“SignalR 类型上不存在属性 'roomHub'。”

我当然不了解有关 TypeScript 的所有内容,但这对我来说似乎不正确。我遇到了编译器错误吗?还是有其他方法可以做到这一点?

【问题讨论】:

  • 有点切题,但我想你可能会感兴趣,我写了一个 T4 模板来从你的集线器生成 .d.ts:gist.github.com/4583549
  • @RobFonseca-Ensor - 进一步证明没有善行会受到惩罚:我刚刚在 Gist 上发布了我遇到的问题 :-)。

标签: signalr typescript


【解决方案1】:

如果SignalR 对象有实际成员,您想改用declare module 语法。 interface 声明仅描述类型上的成员(而不是描述现存对象)。

///<reference path="../Scripts/jquery-1.8.d.ts" />
///<reference path="../Scripts/signalr-1.0.d.ts" />

declare module SignalR {
    var roomHub: Service.RoomHub;
}

export module Service {
    // Good now
    export var roomHub = $.connection.roomHub;
    export interface RoomHub { }
}

【讨论】:

  • 使用那个确切的代码,我得到了同样的错误:$.connection.roomHub 上有一点红色的波浪,以及消息“属性 'roomHub' 在类型 SignalR 上不存在。”而且我认为(我不确定)interface 语法是否合适,因为它描述了$.connection 对象向世界呈现的接口。
  • $.connection 是如何声明的?
  • 它在运行时在文件jquery.signalR-1.0.0-alpha2.js 中定义,并在编译时在定义文件signalr-1.0.d.ts (github.com/borisyankov/DefinitelyTyped/blob/master/signalr/…) 中定义。
【解决方案2】:

我想出了一个解决方法。我将接口提取到一个单独的文件中:

// File: ISignalR.ts
interface SignalR {
    roomHub: RoomHub;
}

interface RoomHub {
}

然后我在我的服务文件中引用了该文件

///<reference path="../Scripts/jquery-1.8.d.ts" />
///<reference path="../Scripts/signalr-1.0.d.ts" />
///<reference path="ISignalR.ts" />

export module Service {
    export var roomHub = $.connection.roomHub;
}

这很有效,很奇怪。我不确定这是编译器错误,还是我继续误解的东西,但它显然与一些与 AMD 模块支持相关的细微语义变化有关。我很想听听比我更了解 TypeScript 和/或 RequireJS 模块的人的更多解释。

【讨论】:

    【解决方案3】:

    连接 SignalR 的方法不止一种,使用 createHubProxyinvoke 对 TypeScript 更友好:

    export class FrameworkHub {
    
        private connection: HubConnection;
        private proxy: HubProxy;
    
        Init(): void {
            this.Connected = false;
            this.connection = $.hubConnection();
            this.connection.logging = true;
            // Binding with createHubProxy means you can use a string name, so no need to add dynamic properties to the hub
            this.proxy = this.connection.createHubProxy("MyHubName"); 
            this.wireEventListeners();
            this.initializeConnection();
        }
    
        // Binding with proxy.on means you can use a string name for the function, so no need to add dynamic properties to the hub.
        wireEventListeners(): void {
            this.proxy.on("HandleFrameworkMessage", (message: IFrameworkMessage) => {
                console.log("HandleFrameworkMessage: " + message.AccountID + " - " + message.ArmID);
                // Do something to handle the message here.
            });
        }
    
        initializeConnection(): void {
            //console.log("Framework Hub initializeConnection");
            var that = this;
            //Again, using invoke means passing a string argument.
            this.connection.start().done(() => {
                that.proxy.invoke("Connect", this.AccountID, this.ArmID).done((response:FrameworkHubResponse) => {
                    //console.log("FHR: " + response.Success + " - " + response.Message);
                    if (response.Success) {
                        // Do something.
                    }
                    else {
                        // Try again. Would be better with some kind of exponential back-off.
                        setTimeout(that.initializeConnection, 500);
                    }
                });
            });
        }
    }
    

    这是一个从真实代码中截取的稍微粗略的示例,但我发现它是使用 SignalR 的最佳 TS 方式。这种连接的文档在这里:https://github.com/SignalR/SignalR/wiki/SignalR-JS-Client-Hubs-%28No-Proxy%29 - 请注意,因为文档并不总是跟上最近的变化。

    【讨论】:

    • 我仍然在关注 SignalR - 那么您认为使用这种方法而不是自动生成代理的优势是什么?如果我将自动生成的代理与 TS 一起使用,是的,我需要手动定义它们的接口 - 但我想我有点喜欢关注点分离,所以我控制的代码中没有任何魔术字符串.当然,理想情况下,让 SignalR 在 JS 代码旁边生成 TS 接口会很棒。
    • 这是风格问题,真的。我还在习惯 SignalR,但以这种方式工作感觉更像是在使用任何其他 PubSub 组件(JS 中的大多数事件处理几乎都是围绕魔术字符串构建的,所以对我来说感觉很正常)。动态对象感觉有点反TS(这是一个悖论,因为JS当然是围绕它们构建的),我将把上面的类变成一个通用的HubManager,我可以将集线器名称传递给它然后只是连接事件监听器。
    • 当然,处理魔术字符串的一种方法是创建一个枚举(目前不是 TS 枚举,因为它们仍处于试验阶段 - 但只是一个具有静态字符串成员的类)。这就是我使用 Azure ServiceBus 所做的,这是另一个基于字符串的 PubSub 系统,它运行良好。
    猜你喜欢
    • 2018-08-24
    • 1970-01-01
    • 1970-01-01
    • 2018-07-25
    • 2015-08-23
    • 1970-01-01
    • 1970-01-01
    • 2015-05-08
    • 2017-09-09
    相关资源
    最近更新 更多