Front-end with TypeScript Tutorial – Step 4: Importing Libraries

Posted by Bogdán Bikics on November 9, 2016

After successfully writing our first working TypeScript application in part 2 (step 1-3), in this post we are going to have a look at how to use JS libraries (in this case Knockout.js) in TypeScript with Typings. We are also going to use Require.js to take care of dependencies.

Step 4: Typings

As I mentioned before, Typings is a definition file manager which helps you to retrieve and organize definition files. It’s much easier than just downloading them manually.

[Note: Although Typescript 2 has built-in typings, at the time of writing it is not a good choice.  It does not contain many definition files and the files are not the best of their kind. I am sure it will improve in the near future.]

Let’s install it right away!

npm install --global typings

Now let’s search for Knockout in the Typings registry:

typings search knockout

This will list a lot of things*. Referring to source dt is kind of a default, but feel free to check the other alternatives. Source dt is the DefinatelyTyped repository with TypeScript type definitions on GitHub.

Install it from DefinatelyTyped (dt~):

typings install dt~knockout --save --global

Let’s see what happened! A folder named “typings” has been created. All the definition files (*.d.ts) are placed here.

Additionally, a “typings.json” has been created in the root folder. It contains all the definitions as dependencies. Typings works similarly to NPM. As in the case of “node_modules”, it is a good idea to include the “typings” folder in your git/hg ignore file. Whenever somebody clones your project, she can simply run typings install, and all the definition files will be downloaded.

Now the time has arrived to let Knockout pay a friendly visit to your TypeScript code. First of all we need to tweak the TypeScript compiler a little bit. Create a file named “tsconfig.json” in the root folder and copy the following code into it:

{
    "compilerOptions": {
        "module": "amd"    
    },
    "files": [
        "./typings/index.d.ts",
        "./typescript/main.ts",
        "./typescript/require-config.ts"
    ]
}

Here we tell the compiler what files to compile on the files section. Please note that there is a “require-config.ts” file in the ‘files’ list, which we will create later.

In the module section we specify that the modules should be handled by “amd” (Asynchronous Module Definition – like RequireJS).

Next up, we need RequireJS:

npm install --save requirejs

Let’s first create the “require-config.ts” file in the “typescript” folder, and then modify “main.ts” and “index.html”:

require-config.ts

declare var require: any;
require.config({
    paths: {
        'knockout': '../node_modules/knockout/build/output/knockout-latest'
    }
});
require(['../typescript/main']); // run main.ts from here.

main.ts

import * as ko from 'knockout';

class ViewModel {
    words = ko.observableArray(['Hello', 'World']); 
}
ko.applyBindings(new ViewModel());

Now we can compile the TypeScript files:

tsc

index.html

<!doctype html>
<html>
<head>
</head>
<body>
    <ul data-bind="foreach: words">
        <li><p data-bind="text: $data"></p></li>
    </ul>
    <script src="../node_modules/requirejs/require.js"></script>
    <script src="../typescript/require-config.js"></script>
</body>
</html>

Open “index.html” in a browser, and you will find that it works like a charm!

As you can see, Knockout is handled smoothly by the tsc compiler. But what if the definition file is too old, and some new features only came out in the latest Knockout release?

Let’s say you want the following:

ko.newGreatStuff();

This will give you a compilation error. Luckily TypeScript doesn’t restrict you to a specific definition file. Here is what you can do:

(ko as any).newGreatStuff();

Here, the Knockout object is being cast to an any type. Tsc won’t ask any questions but will just compile it.

Coming up next

You might have the feeling that handling dependencies is quite a pain already. RequireJS has served its purpose. So in the next step (Step 5: Using Browserify) we will say goodbye to it. Enter Browserify!


Yes, there can be differences in definition files. It can also happen (unfortunately) that something is not defined at all on a given file (a missing method for instance). There is a workaround for this problem, which we will address later.

About the author: Bogdán Bikics

More Posts