Open-Source Internship opportunity by OpenGenus for programmers. Apply now.
Typescript introduces static typing to JavaScript, but it's not clear what this implies until one understands what static typing is all about. Is there some reason to prefer it over vanilla Javascript ? This article intends to be a quick introduction to why typescript (or any typed language for that matter) is a big deal, and then covers TypeScript syntax for those who are migrating from Javascript. You can get started with the language within an hour if you're already using ES6 or above.
Table of contents
1. What's so great about being typed
If you have ever divided a number by zero, or tried to divide 4 by 'apple', you've encountered type errors in your code. Type errors are just a subset of possible errors out there. With an interpreted language like Javascript, all such errors are handled when your code is running. A compiler could have instead looked at your code and pointed out a lot of these type errors. The compiler points out these errors in your code by just looking at it, rather than executing it - called static type checking. Javascript already has type checking, but a dynamic one which doesn't complain until it absolutely needs to.
It will happily evaluate 4/0 to Infinity and 0/0 to NaN. The former result is even debatable (if you know how to take limits, ask yourself whether the limit of 4/x will converge as x tends to zero), and even the NaN value is rather ambiguous. NaN is never equal to itself, and everything from Infinity - Infinity
to "Dogs" - "Cats"
gives NaN. A static type checker will not allow such code, it will throw an error during the compile time, which comes before the code is run. Your likely faulty code is thus saved from being added and causing problems later when you've already forgotten about it.
Sometimes the flexibility can be great too. In my starting days with Javascript, I was delighted at how flexible the language was. Sure, I made mistakes,
but I never thought it the responsibility of the language to correct me. Once you learn to write proper code and avoid the common pitfalls, the flexibility
of a language can directly translate to tighter and more concise code, saving time as well as disk space. However, in large apps, mistakes can get buried
in thousands of lines of code, and so kind of tool you can get to minimise the chances of having to find a bug later in your code is welcomed with the
enthusiasm not present in programmers whose concerns aren't with large applications or those who don't work in large teams. It is easier to tell everyone
to use TypeScript than to force them to correct their code (which requires someone dedicated to reading new code and providing feedback) everytime they make
a change.
I do find the typed syntax slightly less expressive than non-typed syntax. Given the advantages laid out above, the programmer can himself choose which
he prefers - typed or untyped. Incase it is the former and you know Javascript (ECMAScript 6 or above) already, let's dive into the syntax of Typescript.
The syntax
TypeScript is a superset of Javascript. All JavaScript files are valid typescript files - just change the extension from .js
to .ts
. You can thus
opt in as much as you want to or need to. Maybe you'll use one line of typescript specific features and just that - or you could smatter types all over
your code should you prefer that instead. Let's begin with just one line for now.
let x = "Hi";
Nothing new here. But typescript is smart enough to figure out here that x has the type of string. JavaScript has these types too, which you can see by
using typeof. However, since typescript has a compile step that converts your code to vanilla JavaScript, the compiler will complain if you make
a mistake rather than letting your code pass through and become javascript code.
If you're the explicit and verbose kind, then you might have instead written
let x : string = "Hi" ;
which is the same as the first piece of code above. Sometimes you just have to be explicit, like in function parameters.
function add(a : number, b : number ) : number { // a is a number, b is a number and the return value is also a number.
return a + b;
}
If you had not added types, it wouldn't be clear whether a and b are two numbers, strings or arrays, since the function can give a valid result for
all of these cases. In that case, typescript would assign the type any
for these values which is essentially the same as saying "this variable can
have any type value" - something to be avoided. If all your values are of type any
, there is no likely reason for you to continue using typescript
and should migrate to Javascript to save your time wasted in compiling TypeScript.
You can also express "A, B and C have the same type" without saying beforehand what type that would be :
function add<X>(a : X, b : X) : X { // a and b have some type X, as does the return value
return a + b;
}
Maybe I want my function to instead work for arrays strings and numbers. For that, you could create a new type called addable, which allows all
of the three basic types. To check which basic type a value is, just use typeof.
type addable = number | string | any[]; // one of these types
function add( a : addable, b : addable) : addable {
return a + b;
}
But here, typescript throws an error. For good reason, because some operations lead to unexpected results. The result of [1,2] + [3, 4] is not an array
for example. Instead, it is the same as doing "1,2"+"3,4" which gives "1,23,4". We are forced to explicitly check for types in our function. Let's try again.
type addable = number | string | any[];
function add( a : addable, b : addable) : addable {
if (typeof a !== typeof b) throw TypeError(`${a} and ${b} are dissimilar types and can't be added`);
if (typeof a === 'number' || typeof a === 'string') return a + b;
return a.concat(b);
}
Still an error. This time, the error is not of our own fault. Typescript isn't smart enough to figure out just by looking at the code above, that a and b are always the same type after the first line of the function where we throw an error otherwise. This is static vs dynamic type checking for you. If the type checking was dynamic, we could just start executing the code, and complain when we run into an error, which we wouldn't have. But static type checking above is less smart, and expects the above code to lead to errors. For above code to pass the static type check, you'll have to be even more verbose.
type addable = number | string | any[];
function add( a : addable, b : addable) : addable {
if (typeof a === 'number' && typeof b === 'number') return a + b;
else if (typeof a === 'object' && typeof b === 'object') return a.concat(b); // typescript figures this is the any[] case
return a + '' + b;
}
The last line which says a + '' + b
says that even if the types of a and b mismatch, just coerce them to a string. This is different from the code above, which only passed for cases where the types of a and b were the same.
If this verbosity seems to be a little too much, you're not alone. Even I would prefer to just be able to write a quick oneliner saying let a = (a, b)=>a+b;
and take the consequences (there usually aren't any). However, in large apps the type checking can be a boon which prevents you (and people you can't control) from making mistakes. Static type checking isn't necessary for everyone, so it might or might not be for you. I also haven't mentioned interfaces and structural type system anywhere in this article, although those are very important and fundamental concepts. Interfaces allow you to predeclare the type for something, so you have to type lesser and you can reuse your code. Structural typing allows you to pass and return values which aren't explicitly your desired type, but adhere to the same structure as your type. With these concepts, you're equipped to start writing TypeScript, give it a try here. Happy typing.