How can I define an AngularJS service using a TypeScript class that doesn't pollute the global scope? How can I define an AngularJS service using a TypeScript class that doesn't pollute the global scope? angularjs angularjs

How can I define an AngularJS service using a TypeScript class that doesn't pollute the global scope?


2016-05-06: New example using ES6-style modules

The static $inject array and constructor remain unchanged from the previous example.

The only change is to split the classes into multiple files and use ES6 modules to pull in the class definitions.

/lib/HelloService.ts:

export class HelloService {    public getWelcomeMessage():String {        return "Hello from HelloService";    }}

/lib/AnotherService.ts:

import {HelloService} from './HelloService';/** * Service that depends on HelloService. */export class AnotherService {    // Define `HelloService` as a dependency.    static $inject = ['HelloService'];    constructor(        // Add the parameter and type definition.        public HelloService: HelloService    ){}    public getWelcomeMessage():String {        // Access the service as: `this.HelloService`        // Enjoy auto-completion and type safety :)        var helloMsg = this.HelloService.getWelcomeMessage();        return "Welcome from AnotherService, " + helloMsg;    }}

/index.ts:

// Using the services.import {HelloService} from './lib/HelloService';import {AnotherService} from './lib/AnotherService';angular.module('HelloApp', [])    .service('HelloService', HelloService)    .service('AnotherService', AnotherService)    .run(['AnotherService', function(AnotherService: AnotherService){        console.log(AnotherService.getWelcomeMessage());    }]);

Previous answer: using namespaces

Building from Steve Fenton's answer:

To allow dependency injection, add a static $inject array on your class.

See the Angular $injector documentation on how the $inject array works.

The dependencies will be injected into your constructor in the order given by the array (and makes it work with minification).

Dependency Injection Example:

namespace MyModule {    /**     * Angular Service     */    export class HelloService {        public getWelcomeMessage():String {            return "Hello from HelloService";        }    }    /**     * Service that depends on HelloService.     */    export class AnotherService {        // Define `HelloService` as a dependency.        static $inject = ['HelloService'];        constructor(            // Add the parameter and type definition.            public HelloService: MyModule.HelloService        ){}        public getWelcomeMessage():String {            // Access the service as: `this.HelloService`            // Enjoy auto-completion and type safety :)            var helloMsg = this.HelloService.getWelcomeMessage();            return "Welcome from AnotherService, " + helloMsg;        }    }}// Using the services.angular.module('app.services.helloService', [])    .service('HelloService', MyModule.HelloService)    .service('AnotherService', MyModule.AnotherService)    .run(['AnotherService', function(AnotherService: MyModule.AnotherService){        console.log(AnotherService.getWelcomeMessage());    }]);


I should provide what I actually ended doing:

module MyModule {    export class HelloService {        public getWelcomeMessage():String {            return "Hello";        }    }    angular.module('app.services.helloService', []).factory('helloService', () => {        return new HelloService();    });}

In this way I can use

return new HelloService();

instead of

return new MyModule.HelloService();


I have two solutions, the first gives you class-based syntax, the second leaves absolutely nothing in the global scope...

You could compromise slightly by only adding a single handle to the global scope (this really applies if you have multiple classes that you want to avoid placing in the global scope as currently you only have one class).

The following code leaves only the module in the global scope.

module MyModule {    export class HelloService {        public getWelcomeMessage():String {            return "Hello";        }    }    export class AnotherService {        public getWelcomeMessage():String {            return "Hello";        }    }}angular.module('app.services.helloService', []).factory('helloService', () => {    return new MyModule.HelloService();});angular.module('app.services.anotherService', []).factory('anotherService', () => {    return new MyModule.AnotherService();});

Alternatively, to leave not a single thing in global scope, you could avoid the class syntax and use "plain old JavaScript":

angular.module('app.services.helloService', []).factory('helloService', () => {    var HelloService = (function () {        function HelloService() {        }        HelloService.prototype.getWelcomeMessage = function () {            return "Hello";        };        return HelloService;    })();    return new HelloService();});