Search code examples
angularjstypescriptangular-services

AngularJS TypeScript Service Error


I'm getting this error trying to add a service to a module. Can someone help point out what's wrong?

Angular 1.5.11 and TypeScript 2.2.2

ERROR in ./source/mainModule.ts
(185,33): error TS2345: Argument of type 'typeof AwesomeService ' is not 
assignable to parameter of type 'Injectable<Function>'.
Type 'typeof AwesomeService ' is not assignable to type '(string | Function)[]'.
Property 'push' is missing in type 'typeof AwesomeService '.

Here is where I'm trying to add the service

export default angular.module('iris.service', [])
    /* This line throws the error --> */.service('awesomeService', AwesomeService);

In a separate file, here is how I'm creating the service

export class AwesomeService extends OtherClass {

    private static $inject = ['configService'];

    constructor() {
        super();
    }
}

update:

I see that if I change AwesomeService to a function and export that, it works fine. Is there any way I can use a class for a Service? It looks like @types/angular specifies that the second argument to angular.module.service should be either a string or a function.


Solution

  • Yes, you can do exactly what you want, you simply need to write less code.

    The type declaration in @types/angular includes the following

    declare global {
        interface Function {
            $inject?: ReadonlyArray<string>;
        }
    }
    

    This augments the declaration of the Function type. Adding an optional property, $inject, to allow for clean, readable AngularJS DI annotations to be added to classes and functions in a type safe and declarative manner without the need for unsightly type assertions.

    Note that all classes and functions are in fact functions.

    The problem with your class is that, while the Function type augmentation above states that $inject is optional, it does not state that when it is specified, that it may be private.

    In fact, it is very much not private, semantically, as it is read by the AngularJS framework and potentially other tools as well.

    To resolve the issue simply write

    export class AwesomeService extends OtherClass {
    
        static $inject = ['configService']; // no private here.
    
        constructor() {
            super();
        }
    }
    

    The more detailed answer is that two types are considered structurally incompatible if one declares a private member (to do this it needs to be a class) with the same name as a public member in the other (all interface members are public).

    Unfortunately, the error is a bit cryptic. The private $inject declaration causes the type checker to immediately remove the overload of service which takes a value of type Function. It then tries the overload of service which takes a value of type Array and fails.