The one thing that keeps me from using TypeScript

A while ago, I tweeted about the reason why I don’t use TypeScript in more of my personal projects.

It’s not that I dislike TypeScript and that I don’t use it at all. I actually enjoy using it in my Angular 2 projects. The reason I don’t use it more often however, is because because I’m being forced to use type definitions when I want to use 3rd party modules. When it comes to these 3rd party modules, it feels like TypeScript is more all in or nothing in comparison to it’s direct competitor, Flow.

First of all, let me explain what type definitions are. Type definitions or type declarations describe the shape of an external module. It describes the available methods of the module, the return type, the parameters and so on.

To give you an example, let’s assume we use a concat module that concatenates two strings a and b. The definition file would look like this.

After installing the module, together with the type definitions, we can start using it.

That example works perfectly. Both arguments provided, foo and bar, are from the type string and thus match the definition file. So what if we change one of the arguments to be a number instead?

This will result in the following error when transpiling.

index.ts(3,13): error TS2349: Cannot invoke an expression whose type lacks a call signature.

The major benefit of these type definitions is to catch errors early on in the development process, at build time instead of at runtime.

A lot of work has been done by the OpenSource community to create type definitions for numerous projects. The DefinitelyTyped project hosts type definitions for more then 2000 package and recently typings started adding these type definitions to npm inside the @types namespace. With the typings cli tool, you can easily search and install the definition files from both npm and DefinitelyTyped.

But as you will notice, not all the modules you want to use in your project actually have these type definitions. So let’s go back to our previous example.

What would happen if we didn’t had these type definitions for our concat module?

index.ts(1,25): error TS2307: Cannot find module 'concat'.

Exactly, the transpiler will fail because it does not recognize the concat module.

From this point on, you have two options. The first option would be to create the entire definition file yourself and publish it to npm or DefinitelyTyped. While this could take some time, it could be beneficial in the long run, especially when you use that module a lot. This approach is perfectly feasible for small one-line modules, but not so much for very large modules like for example axios. In that case, you would probably go for option number two, the easy way. You can always define a module as type any. In TypeScript 2.0, the syntax is even made a little bit easier and looks like this.

The downside of defining a module as any, is that you don’t get any compile-time feedback. Previously, when we passed a number to our module instead of a string, the transpiler failed. With the module being defined as any, you can do whatever you want with it. It will compile but might throw an error at runtime, just like it does in vanilla JavaScript.

Because it is the fastest and easiest way of solving this, the any-type approach is likely the one being used the most.

A great benefit when installing type definitions with typings is that it will create a typings.json file for you. This file keeps track of what type definitions are installed in your project. This way, you don’t have to commit all the type definitions to git. Except for the ones you created manually of course.

So now I want to come back to my tweet I mentioned in the beginning of this post. The reason why I don’t use TypeScript in more of my personal projects, although I like the language. The type definition eco system will always run behind. Maybe there aren’t definitions for the modules you use, and if there are, they might be outdated. This keeps me from gradually and fastly switching over from JavaScript to TypeScript. When I make the choice of switching over, I want to do that as quickly and smoothly as possible. I want to be able to write only a couple of functions in TypeScript and translate my project bit by bit without having to worry about typings I can’t find immediately. And when the time has come, I might be writing those definition files for the modules that doesn’t have them. This is perfectly possible with Flow which doesn’t force you to install those external type definitions and which makes gradually switching over a breeze. The benefit is that you can prototype quickly and make it stricter later on with support for type definitions of external modules.

So TypeScript team, That’s why I have a suggestion for you. You can already turn off the noImplicitAny flag which basically assumes that all the variables without explicitly declared type are of type any. Why couldn’t we add an extra flag, a noImplicitAnyModule flag? When you turn off this flag, it will assume that every module without type definition is of type any. And just add a warning to the console while transpiling that this flag is being turned off and should be used with care. Just to make sure that developers know what they are doing.

I believe that my suggestions will make TypeScript easier to adopt for beginners. That it will make gradually switching your project from JavaScript to TypeScript easier. I hope that TypeScript will be less all or nothing!

Yours sincerely!

Sam