无法使用TypeScript修饰AngularJS的ExceptionHandler,因为函数不被识别

时间:2017-03-13 13:48:38

标签: angularjs typescript exception-handling

背景
在我正在开发的项目中,我们已经从使用带有JavaScript的AngularJS(1.6.2)切换到TypeScript 2.1.5。

我们有一个应用于$exceptionHandler服务的装饰器,它导致JavaScript异常调用通用API,向开发团队发送电子邮件;通过这种方式,我们可以很容易地看到最终用户在野外遇到的前端错误。

问题:
我最近将这个装饰器从JavaScript转换为TypeScript。当我尝试运行应用程序时,我遇到了什么都没有的白屏。经过大量调试后,我发现问题是因为AngularJS希望$provide.decorator传递一个函数以及依赖项列表。但是,正在观察一个对象,从而迫使Angular进行故障安全。

我通过在angular.js内部设置断点来诊断问题;由于抛出的,未处理的JavaScript异常,它特别会在第4809行(函数createInternalInjector(cache, factory)内部)失败,但实际负责失败的部分是函数invoke(fn, self, locals, serviceName)内的第4854行。它失败的原因是因为传递的依赖关系是['$delegate', '$injector',];该集合缺少该功能。

最后,我考虑做的一件事就是在类代码中定义一个JavaScript函数。由于两个原因,这在我的案例中不起作用。首先,在我们的ts.config中,我们将noImplicitAny设置为true;函数隐含于any类型。此外,TypeScript本身似乎不会将function识别为关键字,而是尝试将其编译为类ExceptionHandler上的符号而无法将其编译。

TypeScript异常处理程序:

export class ExceptionHandler {
    public constructor(
        $provide: ng.auto.IProviderService
    ) {
        $provide.decorator('$exceptionHandler`, [
            '$delegate',
            '$injector',
            this.dispatchErrorEmail
        ]);
    }

    public dispatchErrorEmail(
        $delegate: ng.IExceptionHandlerService,
        $injector: ng.auto.IInjectorService
    ): (exception: any, cause: any) => void {
        return (exception: any, cause: any) => {
            // First, execute the default implementation.
            $delegate(exception, cause);

            // Get our Web Data Service, an $http wrapper, injected...
            let webDataSvc: WebDataSvc = $injector.get<WebDataSvc>('webDataSvc');

            // Tell the server to dispatch an email to the dev team.
            let args: Object = {
                'exception': exception
            };
            webDataSvc.get('/api/common/errorNotification', args);
        };
    }
}

angular.module('app').config(['$provide', ExceptionHandler]);

原创JS:

(function () {
    'use strict';

    angular.module('app').config(['$provide', decorateExceptionHandler]);

    function decorateExceptionHandler($provide) {
        $provide.decorator('$exceptionHandler', ['$delegate', '$injector', dispatchErrorEmail]);
    }

    function dispatchErrorEmail($delegate, $injector) {
        return function (exception, cause) {
            // Execute default implementation.
            $delegate(exception, cause);

            var webDataSvc = $injector.get('webDataSvc');

            var args = {
                'exception': exception,
            };
            webDataSvc.get('/api/common/ErrorNotification', args);
        };
    }
})();

问题:
1.我可以用什么方式重写TypeScript异常处理程序,以便AngularJS正确选取? 2.如果我不能,这是一个需要升级的AngularJS错误吗?我知道事实上我不是唯一一个使用TypeScript的AngularJS的人;由于语言选择而无法装饰服务似乎是一个非常重要的问题。

2 个答案:

答案 0 :(得分:2)

将所有内容转换为类不是TS的目的,这里的类没有用处。 JS代码应该使用类型进行扩充,并且可能使用$inject注释进行增强。

angular.module('app').config(decorateExceptionHandler);

decorateExceptionHandler.$inject = ['$provide'];

export function decorateExceptionHandler($provide: ng.auto.IProviderService) {
    $provide.decorator('$exceptionHandler', dispatchErrorEmail);
}

dispatchErrorEmail.$inject = ['$delegate', '$injector'];

export function dispatchErrorEmail(
    $delegate: ng.IExceptionHandlerService,
    $injector: ng.auto.IInjectorService
): (exception: any, cause: any) => void { ... }

config需要常规函数,而不是构造函数。并且原始TS代码失败的原因是ExceptionHandler未使用new调用,因此this不是对象,this.dispatchErrorEmail不是函数。< / p>

答案 1 :(得分:1)

以下是使用TypeScript namespaces执行此操作的另一种方法。对我来说感觉有点干净,保持异常扩展功能是孤立的,并且在概念上来自C#之类的很好。

exception.module.ts

import * as angular from 'angular';
import { LoggerModule } from '../logging/logger.module';
import { ExceptionExtension } from './exception.handler';

export const ExceptionModule = angular.module('app.common.exception', [LoggerModule])
    .config(ExceptionExtension.Configure)
    .name;

exception.handler.ts

import { ILoggerService } from '../logging/logger.service';

export namespace ExceptionExtension {
    export const ExtendExceptionHandler = ($delegate: ng.IExceptionHandlerService, logger: ILoggerService) => {
        return function (exception: Error, cause?: string): void {
            $delegate(exception, cause);
            logger.error(exception.message, "Uncaught Exception", cause ? cause : "");
        }
    };
    ExtendExceptionHandler.$inject = ['$delegate', 'ILoggerService'];

    export const Configure = ($provide: ng.auto.IProvideService) => {
        $provide.decorator('$exceptionHandler', ExtendExceptionHandler);
    };
    Configure.$inject = ['$provide'];
}