1.11. Creating and distributing type declaration files

A very large number of type declaration files are available online but sometimes we may need to work with a JavaScript library or a plugin for which there are no type declaration files available. In that case, we will have no other option than creating the type declaration file ourselves.

How to do it

If you need the type declaration file of a JavaScript library the first thing that you should do is check if it is available at type search i.e. https://aka.ms/types. If no type declaration files are available, below are the steps to create it.

  1. You can start by declaring a variable of type any:
    declare var $: any;
    
  2. At this point you will not get more compilation errors but you may encounter run time errors as the jQuery object will be treated as a variable of type any.
  3. You can then continue by declaring the definitions of the parts of the library that you use:
    interface JQueryStatic {
      ajax(settings: JQueryAjaxSettings): JQueryXHR;
    }
    inferface JQueryAjaxSettings { /*...*/ }
    inferface JQueryXHR { /*...*/ }
    declare var $ : JQueryStatic;
    
  4. As you use more and more parts of the library, you will slowly define its entire public interface. At this point you may wonder if you should declare the interface of private members as well.
    Fortunately, there is a rule that can help us to decide whether we should declare the interface of a private member or not.
    When a member is private because it has been declared inside a closure, you should avoid declaring its interface. For example, in the following code snippet the variable _counter is declared inside a closure:
    var Counter = (function () {
     return (function(){
         var _counter = 0;
         function Counter() {
             _counter = 0;
         }
         Counter.prototype.increment = function () {
             _counter = _counter + 1;
             return _counter;
         };
            
         return Counter;
     })();
    })();
    var c1 = new Counter();
    c1.increment();  // 1
    c1._counter; // undefined
    

    If the member is private because a closure that hides it from the outside world, you should avoid declaring it in your type definition files:

    declare class Counter {
      private _counter: number;
      public increment(): number;
    }
    

    We can also declare the class Counter without using closures to hide the _counter variable:

    declare function count: () => number;
    var Counter = (function () {
     function Counter() {
         this._counter = 0;
     }
     Counter.prototype.increment = function () {
         this._counter = this._counter + 1;
         return this._counter;
     };
     return Counter;
    })();
    var c1 = new Counter();
    c1.increment();  // 1
    c1._counter; // 1
    
  5. If the member is mean to be private but it is accessible as it has been added to the prototype of the object you should declare it in your type declaration files:
    declare class Counter {
      private _counter: number;
      public increment(): number;
    }
    

    After some time, you will probably have a nearly complete type declaration file. If that is the case, please consider sharing it online as open-source so other developers can enjoy the results of your hard work. Just remember that every time you didn’t need to write a type declaration file is because someone else decided to share their work with the community.

How it works

Creating a type declaration file is just declaring the public interface or API of an external software component, in most cases this is a hand-made process but sometimes it can be automated.

For example, by default, the TypeScript compiler includes a .d.ts file known as the default library. This file declares the public API of the DOM and because the DOM is really well defined and is static enough the TypeScript team is able to auto-generate its type declaration file.

You can use the compiler option –noLib to avoid including the default library which contains the DOM type definitions.

There’s more

Since the TypeScript release 1.6, Microsoft is encouraging the inclusion of the type declaration files within the npm modules.

The TypeScript 1.6 or higher compiler uses a different set of rules to resolve module names when targeting commonjs. These rules attempt to model module lookup procedure used by Node.

See also

Please refer to http://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html to learn more about the module resolution rules.


Shiv Kushwaha

Author/Programmer