How to Write TypeScript Ambients Types Definition for a JavaScript library
By: Andréas Hanss
Tags: Software Engineering, Software Development, Web Development, JavaScript, Typescript

Tips & tricks

How to Write TypeScript Ambients Types Definition for a JavaScript library

Types definitions made easy for any JavaScript library. Create, extend, and contribute to any repository where types are missing.

It has been a complicated time during the early days of TypeScript. Originally designed to work with namespace and some kind of “custom modules”, nowadays the best approach is to play with ES6 modules.

How modules and code isolation is managed will influence how we have to write our ambient type definition for our libraries.

Let’s dig into it!

A quick remember about the basics of a compiler

Before diving into the question of TypeScript typings, I would like to clarify things as it might not seem odd for every developer.

Maybe just like me, you were doing something else in the classroom when the teacher was trying to learn you the C language or some kind of compiled language.

We need to understand compilation because while declaring our types definitions we will need to have a good understanding of the scope we are playing with.

By understanding I mean, what is TypeScript exactly doing when he “compile” your code? Or should I say: what is any compiler really doing?

Most of the time by default and without scoping mechanics, all the code you write is globally scoped, even if defined in multiple files: sharing variables and functions.

That’s why in the JavaScript early days we used to make something called « The module Pattern » within the browser page global context. As people tended to use multiple scripts from various locations, which resulted in code collision.

This was generally implemented in such a way, that you are using some kind of “Immediately Invocated Function Expression” to populate a global object with a scoped API. This mimics the behavior of scope.

window.app_modules = window.app_modules || {}; app_modules.MyModule = (function() {

var privateVariable = 1;



var publicFunction = function() {

return privateVariable;

} return {

publicFunction: publicFunction

};

})(); console.log(app_modules.MyModule.publicFunction());

Well, something like this that emulate private properties. Allowing to play with JavaScript scope to prevent variable to play globally between modules.

One job of a compiler is to take multiple files into one file and ensure correctness between declarations.

So a compiler takes a workspace of files, merge all files into one, and once this has been done, it might also convert the code to some byte code that is highly optimized (not our case with TypeScript).

What does that mean?

It means that your JavaScript file, if not properly scoped is sharing the same variables between files. It’s not a good thing because it can trigger some kind of non-expected effects. But with ES6 modules, which is a compatible feature of TypeScript there is a particularity.

A long as you import or export something in a file, your code is scoped to that file. Variable declarations, functions, anything. Otherwise, just try to create two files without export, one creating a variable toto and another that displays this variable, everything without export in the same namespace, compile it with TypeScript and you get the value of toto colliding in both files

To solve these issues we can play with namespace, which creates some kind of boundaries for scoping things (it works well in many programming languages), use the module pattern we saw above, or just export/import something thanks to EcmaScript modules.

WELL! Let’s deep into our real need: Creating a type definition

Fetching the good declaration for a TypeScript library

Alright, now we got the basics, we can play with our typings that use our scope. We just downloaded our latest library, but there is an issue… The library creator made all of this without TypeScript! (YEAH it still exists in 2020).

For example with a library named somelibrary you end up with the classic

Could not find a declaration file for module 'somelibrary'. '/Users/screamz/workspace/myapp/node_modules/somelibrary/index.js' implicitly has an 'any' type.

Try `npm install @types/somelibrary` if it exists or add a new declaration (.d.ts) file containing `declare module 'somelibrary';`ts(7016)