Type definitions for popular JavaScript libraries

As we have seen, TypeScript has the ability to annotate JavaScript, and bring strong typing to the JavaScript development experience. But how do we strongly type existing JavaScript libraries? The answer is surprisingly simple: by creating a definition file. TypeScript uses files with a .d.ts extension as a sort of header file, similar to languages such as C++, in order to superimpose strong typing on existing JavaScript libraries. These definition files hold information that describes each available function and or variables, along with their associated type annotations.

Let's have a quick look at what a definition would look like. As an example, consider a function from the popular Jasmine unit testing framework called describe:

var describe = function(description, specDefinitions) { 
  return jasmine.getEnv().describe(description, specDefinitions); 
}; 

Note that this describe function has two parameters – description and specDefinitions. But JavaScript does not tell us what sort of variables these are. We would need to have a look at the Jasmine documentation to figure out how to call this function: If we head over to http://jasmine.GitHub.io/2.0/introduction.html , we will see an example of how to use this function:

describe("A suite", function () { 
    it("contains spec with an expectation", function () { 
        expect(true).toBe(true); 
    }); 
}); 

From the documentation, then, we can easily see that the first parameter is a string, and the second parameter is a function. But there is nothing in JavaScript that forces us to conform to this API. As mentioned before, we could easily call this function with two numbers, or inadvertently switch the parameters around, sending a function first, and a string second. We will obviously start getting runtime errors if we do this, but TypeScript, using a definition file, can generate compile-time errors before we even attempt to run this code.

Let's have a look at a piece of the jasmine.d.ts definition file:

declare function describe( 
    description: string,  
    specDefinitions: () => void 
): void; 

This is the TypeScript definition for the describe function. Firstly, declare function describe tells us that we can use a function called describe, but that the implementation of this function will be provided at runtime.

Clearly, the description parameter is strongly typed to be a string, and the specDefinitions parameter is strongly typed to be a function that returns void. TypeScript uses the double braces () syntax to declare functions, and the arrow syntax to show the return type of the function. Hence, () => void is a function that does not return anything. Finally, the describe function itself will return void.

Imagine that our code were to try and pass in a function as the first parameter, and a string as the second parameter (clearly breaking the definition of this function), as shown in the following example:

describe(() => { /* function body */}, "description"); 

In this instance, TypeScript will generate the following error:

hello.ts(11,11): error TS2345: Argument of type '() => void' is not assignable to parameter of type 'string'.  

This error is telling us that we are attempting to call the describe function with invalid parameters. We will look at definition files in more detail in later chapters, but this example clearly shows that TypeScript will generate errors if we attempt to use external JavaScript libraries incorrectly.