Importing third party libraries into Angular applications: a quick venture into TypeScript

So working with Angular is pretty awesome. Having worked with other client side SPA frameworks and MVC frameworks on the server side I have truly come to appreciate the problems Angular solves for us and the way it solves said problems. As great as Angular is though, sometimes you'll still have those head scratching moments.

One of the little errors that I found to be strangest when I first started with Angular 2 was this little Typescript error you get when you try to use third party libraries in your project. Today, I was thinking to myself that I should write a post on this topic to quantify my thoughts. Not even two hours later, a friend shot me a text asking questions on this very topic. When doing my own research initially it seemed like this was a pretty common issue. I hope this helps to clarify things for some people. So let's dig in.

The Problem: A Typescript Error that's not super intuitive and code that won't compile.
Uncaught Error: Module build failed: Error ... Cannot find name '$'  

Let me take a little aside here before we proceed. The Angular folks who have worked with the framework for even a short length of time are probably asking why I would be trying to import jQuery in the first place. Technically speaking, it's an anti-pattern to use jQuery in Angular projects unless you need it for a very specific reason. Among other features, Angular already has abstractions for ajax requests and DOM manipulation, two of the more frequently used parts of jQuery. With that aside though, sometimes you may just want a quick and dirty solution or you're just using a lib that has jQuery as a dependency, then we'll need to figure out how to get jQuery to play nice.

In one of my projects I wanted to use SemanticUI. It's a pretty awesome UI framework if you haven't used it. I prefer it to Bootstrap, although if you're using Angular you probably want to take a look at Angular Material. Anyway, SUI has a dependency on jQuery hence my need to have it in the project. Ok so let's cut to the chase.

Why do you get the error?

So why do you get this error? The reason is that Typescript can't find any type definitions associated with that jQuery by default. Let's take that a step further though and explore a little deeper. The way Typescript works ( TS from here on out ), is that each data type that you're working with, each class, has TS definitions associated with it. The are called typings. These typings are in TS definition files that are suffixed like so: <file_name>.d.ts. When we import jQuery, it doesn't come with any typings since it's not written in TS. So essentially, it's not really an error. This is just TS's way of saying "Hey, I don't know what this is!". So that's nice and all but how do we correct this so we can get our code to compile and stop yelling at us?

First Glance solution: Declare the type.

So there are a few ways to crack this nut. The first one is this, if you only need the library in question in a few places, you can just declare the data type at the top of your class or file you're working with like so:

//... more code above

declare var $:any;

ngAfterViewInit(){  
   $('.someCoolElement').goesBam();
}

This let's TS know not worry about $ he's legit. This solution will get you by but it's not ideal. I say that because if you have code in multiple places that relies on this typing, then you will have to write that over and over again all over your app. Ain't nobody got time fo that! Plus it looks ugly.

Better approach: install some typings

So a more ideal way to go about solving this is just to teach TS what $ is. Teach it to recognize jQuery and what API to expect from it. The way you do this is to install the typings. You can do this via NPM. To solve our particular case all you have to do is install the typings like so: npm install @types/jquery --save. Then restart your ng serve if you're using the CLI. Boom, now you can eliminate all of those nasty inline declare statements in your .ts files.

I like to know how things work, and if you're anything like me, you may be asking yourself "Where do these installed typings go?". The answer, like most things in JS nowadays, is to the node_modules folder, where else ?!?! That's not a complete picture though. They go specifically to node_modules/@types.

There used to be typings managers like one creatively called typings or another popular manager called TSD. These still can be used but TS's official way of handling typings now is via NPM.

As a final note on installing and working with typings, the tsconfig.json has many options for working with types. For instance, typeRoots will tell TS not to look in the default @types directory and instead look elsewhere while the types property will only include values that are specified explicitly while still looking for those in the default location. There are other options too. You should check them all out to get a good view on how things work.

What about SemanticUI: Still not a perfect fix?

So now we don't have any jQuery errors but we now get a new error.
.popup is not defined on $. Basically TS now knows about jQuery, but it doesn't know anything about the library that depends on jQuery, in our example, SemanticUI. So how do we resolve that? If you guessed installing typings for that as well you would be correct. A simple typings install semantic-ui and we're in business.

Conclusion: Looking ahead this may be redundant soon

In newer versions of TS (2.1+), TS is just going to type 3rd party modules without types as any so it won't throw errors. This will work as long as you have the noImplicitAny compiler option set to false.

I hope this article was helpful and as always, if you have any questions or find any errors leave me a some feedback in the comments!