I have some legacy AngularJs code which I am trying to document for a new project of ours using Typescript, but I have run into a bit of an odd case and I am facing a problem.
Essentially we have a Service/Instance of a “class” which has some properties, these properties are actually classes. A Basic example would maybe look like this:
function MySingletonService() { function SomeClass(...) { ... } SomeClass.parse= function() { ... return new SomeClass(...); }; this.SomeClass = SomeClass; } angular.module(...).service('MySingletonService', MySingletonService); // Inside a RUN call here, for demonstrative purposes in this issue, it would be used as: // The actual use in the legacy project happens in other services, directives, components etc. angular.run(['MySingletonService', function(service) { var instance1OfClass = new service.SomeClass(); var instance2OfClass = new service.SomeClass(); var instance3OfClass = service.SomeClass.parse(...); }]);
But I am a bit at a loss on how to document the “MySingletonService” in a typescript .d.ts file. Normally I would just declare classes and interface for the service, like so:
export declare class SomeClass { constructor(...); static parse(...):SomeClass; } export declare interface IMySingletonService { SomeClass: ???; }
If I define it as SomeClass: SomeClass
, then obviously we see it as an Instance of that class, but here it’s the actual class that is assigned to the Property. As such, I have not been able to find an example that looks like what I have.
Searching for just that has proven a bit hard as it basically ends up with examples of how to define the property as an instance if the class, which is straight forward.
It looks like I can get fairly close if I accept having to “Double Document” my code like so:
export declare class SomeClass { constructor(...); static parse(...):SomeClass; } export declare interface IMySingletonService { SomeClass: { new (...): SomeClass; parse(...): SomeClass; }; }
But isn’t there a better way?
Answer
typeof SomeClass
The type SomeClass
describes an instance of SomeClass
. In order to describe the class itself you should use typeof SomeClass
.
This type includes the constructor and all static methods. So you can call new service.SomeClass()
and also service.SomeClass.parse()
.
declare class SomeClass { constructor(); static parse(): SomeClass; } export declare interface IMySingletonService { SomeClass: typeof SomeClass; } declare const service: IMySingletonService; const obj = new service.SomeClass(); // obj has type SomeClass const parsed = service.SomeClass.parse(); // parsed has type: SomeClass
Per the docs (previous version):
Next, we then use the class directly. Here we create a new variable called
greeterMaker
. This variable will hold the class itself, or said another way its constructor function. Here we usetypeof Greeter
, that is “give me the type of theGreeter
class itself” rather than the instance type. Or, more precisely, “give me the type of the symbol calledGreeter
,” which is the type of the constructor function. This type will contain all of the static members ofGreeter
along with the constructor that creates instances of theGreeter
class. We show this by usingnew
ongreeterMaker
, creating new instances ofGreeter
and invoking them as before.