If you haven’t already gotten involved with it, you’ll probably know that TypeScript is becoming increasingly popular. Being able to use a superset of JavaScript in a typed language that compiles down to JavaScript is a great thing. However, if you’ve ever played around with TypeScript and tried to use JavaScript libraries, you’ll probably know that sometimes it can be a real pain. Often JavaScript libraries do not ship with type definitions which are critical when it comes to playing nice with TypeScript.
If you’ve been keeping up with The Polyglot Developer you’ll probably remember two posts that were created. Previously I had written about including external JavaScript libraries in an Angular application as well as adding type definitions to external JavaScript libraries in TypeScript.
We’re going to revisit these two articles and explore all the ways to include JavaScript libraries in TypeScript applications. These include applications built with NativeScript, Ionic, and Angular.
We’re going to use a particular JavaScript library for the examples used throughout this particular article. This library is called base-64 and will handle base64 encoding and decoding without having to worry about atob
and btoa
. You have to remember that this library is a JavaScript library and was not built with TypeScript in mind.
Several different project examples will be created in an effort to keep things easy to understand.
The first project we create will use vanilla TypeScript and will be ran through Node.js. This means we won’t be using Angular, HTML, or anything else. We will create a TypeScript file, compile it, and run it via Node.js.
Create a new project directory and execute the following commands:
npm init --y
tsc --init
The above two commands should leave us with a package.json file as well as a tsconfig.json file. The next step is to add the libraries we wish to use via the Node Package Manager (NPM):
npm install base-64 utf8 --save
The above command will add the base-64 library and its utf8 dependency. As of right now we have a few JavaScript dependencies and our project, but no source code to our application.
Create and open an app.ts file and add the following TypeScript code:
import * as base64 from "base-64";
import * as utf8 from "utf8";
var str = "nicraboy";
var bytes = utf8.encode(str);
var encodedStr = base64.encode(bytes);
console.log(encodedStr);
The above code was taken from the library documentation. However, if you try to compile this file by executing tsc, you’ll end up with errors that look like the following:
app.ts(1,25): error TS2307: Cannot find module 'base-64'.
app.ts(2,23): error TS2307: Cannot find module 'utf8'.
The above is not something we want to see. So what if we altered the library imports to look like the following instead?:
var base64 = require("base-64");
var utf8 = require("utf8");
The above lines are what you’d typically see in a JavaScript application, not necessarily a TypeScript application. When we try to compile our application with the require
statements, we end up with the following messages:
app.ts(1,14): error TS2304: Cannot find name 'require'.
app.ts(2,12): error TS2304: Cannot find name 'require'.
The above lines are different errors, but still errors nonetheless. This is because the require
function is a JavaScript thing, not a TypeScript thing.
So what do we do if we want to resolve these errors and use our library?
One solution would be to download type definitions for each of our libraries if they exist. An example of this would look like the following:
npm install @types/base-64 @types/utf8 --save
If we download the type definitions we can continue to use the import
statements that we saw previously. Execute the tsc
command and it should compile without issues. You can test the project by executing the following:
node app.js
However, what happens if the type definitions don’t exist or what if we’d like to use the require
commands instead? We can actually obtain the type definitions for the require
command so it becomes TypeScript compatible.
Install the following type definitions to use the require
command:
npm install @types/node --save
With the node
type definitions we can use any JavaScript library in our TypeScript project without needing to find the relevant type definition files.
If you’re developing web applications, you might run into a situation where you include a JavaScript library via a <script>
tag that doesn’t play nice in your TypeScript code.
Let’s create a new project somewhere on our computer. We’re not going to create a Node.js project, but we will need to handle TypeScript. Execute the following:
tsc --init
We’ll also need an app.ts and an index.html file in our project. Finally, since this a browser based project, we need to download the base-64 library and utf8 library. This should leave you with a base64.js and utf8.js file in your project.
Open the index.html file and include the following code:
<html>
<head>
<script src="utf8.js"></script>
<script src="base64.js"></script>
<script src="app.js"></script>
</head>
<body onload="load()">
<p id="encoded"></p>
</body>
</html>
In the above HTML we are including both JavaScript libraries and the compiled TypeScript file. In the core content area of the HTML file we have a paragraph tag with an id. This tag will be populated with text when we are done. The load
method is tied to our TypeScript code.
Our TypeScript code in this scenario will look like the following:
declare var base64: any;
declare var utf8: any;
var load = function() {
var str = "nicraboy";
var bytes = utf8.encode(str);
var encodedStr = base64.encode(bytes);
document.getElementById("encoded").innerHTML = encodedStr;
}
Notice that we’re not using import
or require
commands. We’ve already included the script files, but we need our TypeScript logic to understand what we’ve done.
By declaring a variable as any
we are basically telling TypeScript to ignore what the variable is, but recognize that it is a variable. The name of these variables are important and they must match that of the JavaScript library. Running tsc
should succeed in compiling the TypeScript file.
So what if you’re trying to use a JavaScript library in an Ionic with Angular mobile application? A lot of the same rules can be applied, but for clarity, let’s review anyways.
With the Ionic CLI installed, execute the following commands:
ionic start BaseProject blank --v2
cd BaseProject
ionic platform add ios
ionic platform add android
The --v2
tag indicates we are creating an Angular project. I’ve added two platforms, but they are outside the scope of our example. We’re just trying to prove how to use JavaScript libraries.
Now let’s download the two libraries for encoding things as base64:
npm install base-64 utf8 --save
So what are our options when it comes to using these libraries? As of right now we’ve not downloaded any TypeScript type definitions, they are only JavaScript format.
Well, in Ionic you don’t need to download type definitions for TypeScript projects. This is because of what is found in the project’s src/declarations.d.ts file. With a standard project, this file contains the following line:
declare module '*';
The wildcard allows third party JavaScript libraries to be used even if they don’t include type definitions. This means the following would be acceptable in the project:
import * as base64 from "base-64";
import * as utf8 from "utf8";
If the JavaScript library you’re using does have available TypeScript type definitions available for download, they can be downloaded and used like previously demonstrated. It is your choice.
Ionic is a popular framework that uses TypeScript, but so is NativeScript. Pretty much the same rules apply when working with external JavaScript libraries, but we’ll review them anyways.
With the NativeScript CLI installed, execute the following:
tns create BaseProject --ng
cd BaseProject
tns platform add android
tns platform add ios
The --ng
tag above indicates we are going to be using a TypeScript project with Angular.
With the project created we can proceed to installing both our JavaScript dependencies. From the Command Prompt or Terminal, execute the following:
npm install base-64 utf8 --save
Again you’ll notice that the above command does not include the TypeScript type definitions. If you wanted to, you could download the type definitions as previously demonstrated. With the type definitions installed, you could use the same classic TypeScript import
statements:
import * as base64 from "base-64";
import * as utf8 from "utf8";
However, if there are no type definitions available, where does that leave us?
In NativeScript there is no wildcard to allow JavaScript libraries without type definitions. This leaves us with the good old fashioned require
command that you’d find in JavaScript. The good news is that we don’t need to install the node
type definitions. The require
command works out of the box with NativeScript. So if you wanted to, just include these lines:
var base64 = require("base-64");
var utf8 = require("utf8");
At this point the project can be built with no TypeScript compiler errors.
You just saw several different ways to include external third party JavaScript libraries in a TypeScript project. These steps must be noted because trying to use JavaScript libraries in a TypeScript project will generally lead to errors at compile time. By downloading type definitions or using the methods described in this article, you can prevent these errors and use them without issue.
This article was a followup to my very popular articles that were previously written on the subject. Previously I had demonstrated how to include JavaScript libraries in an Angular application as well as how to add type definitions to a TypeScript project. This article is a much more advanced version with more options.