So originally I was resistant to even learn TypeScript because I wasn’t comfortable with strong typed languages and I really tried to avoid writing any more code that I have to. But this is the situation where writing a little more code upfront will pay big devidends as your project grows.
Benefit
The biggest benefit is actually just tooling that you get in your IDE like vs code. When you use type annotation or work with strong typed library, your code will be documented in the IDE so you really have to refer back to online documentation for the libraries that you use. In addition the compiler that can catch bugs in advance which is a far more efficient way to refactor code.
Another cool benefit of typescript is that there’s virtually no learning curve if you know JavaScript. That’s because it’s a superset of JavaScript. So any valid JS code is also valid in TypeScript. You can learn it incrementally as you go. And it also allows us to write code with future JavaScript features without having to worry about whether or not this code will be supported in our environment because we can transpile it to multiple JavaScript flavors.
Install TypeScript
Now that you know TypeScript is awesome, let’s go ahead and started. The first you want to do is install TypeScript globally with NPM. Doing this will give you access to the tsc
command which will run the typescript compiler.
Compile TypeScript to JavaScript
So the first we’ll do is create an index.ts
file and TypeScript on it’s own can’t run anywhere. It won’t work in the browser or Node.js or anything like that. What we do is use the TypeScript compiler to convert TypeScript code to vanilla JavaScript.
Let’s start by writing some plain JavaScript in our TypeScript file and then compile it.
Go down to command line and run tsc index.ts
.
You’ll notice it creates an index.js file that’s our actual JavaScript code which we can run in the browser or node. And because we just wrote plain JavaScript, the code is identical to what’s in the index.ts
file.
By default, TypeScript will compile to ES3 which dosn’t support for async await. So let’s see what’s happen when we write an async function in our .ts
file and the compile it.
You’ll notice here that our code gets transpiled to this creazy looking JavaScript so we can use async await in our main TypeScript code.
Compiler
The compiler is basically very sophisticated. There’s a ton of different options that you can pass to customize it’s behavior. You could pass the options from the command line but the standart to do it is to create a tsconfig.json
which will automatically get picked up when you run tsc
command.
The tsconfig.json
can be be pretty overwhelming at first but there is usually only this options that you have to think about for the most part.
target
The first one target and this is the flavor of JavaScript that your code will compiled to. So if we set target to esnext
and then run tsc, you’ll see that it compiles our code with async await natively. It’s targeting the latest verison of JavaScript which supports that syntax.
watch
Another option that we want to set right away is "watch": true
which will just recompile our code every time we save the file. It will just save us from rerunning the tsc
command after every change.
lib
The next option we will look at is lib which allows us to automatically include typings for certain environtments such as the DOM
or ES 2017
. So if you’re building a web application you’d want to include that DOM library which allows TypeScript to compile your code with all the native DOM classes without any compilation error.
For example if we go back to our code, we can use URL class which is part of the DOM and we’ll got autocomplete and intellisense on this class. So this is where the incredible tooling of TypeScript starts to come in. If we hover over the class we have intergrated documentation as well as an error message telling us exactly why this code won’t run.
Type
Now we know how the TypeScript compiler works, let’s go ahead and write some code that uses type annotations. There are two ways you can strong type your code implicitly or explicitly.
Implicit Type
Let’s say we have a variable that should be a number. If we assign the value to this variable when it’s declared. it’s type will automatically be inferred.
Then if we try to assign a string value to this variable it’s going to give us an error because a string is not assignable to number. If this code were vanilla JavaScript we wouldn’t catch this bug until we actually run this code somewhere. But with TypeScript we know about it right away.
Unlike languages like C# or Java, we can actually opt out the type system annotating our variable with any
.
This just means that this variable can be assigned any value and the compiler won’t type check it. Ideally you want to avoid doing things like this when possible but it does give TypeScript a ton of flexibilty.
Explicit Type
In above example we gave our variable an impicit number type, but what if we don’t have a value to assigned to it upfront. If we don’t add any type annotations, it’s going to be infered as an any
type, so we can assign both a string and number to it. If we want to annotate it with a type, we can just write colon followed by number
which is one of the built-in primitive types in JavaScript. When we do that, we get an error under the string value because we can’t assign it as that type.
Define Custom Types
So we’ve looked at some of the built-in types in JavaScript. But you can also create your own types. First you’ll give the type a name which is typically in Pascal case.
Then we can declare a variable that’s annotated with this Style
type and then we’ll get feedback for this custom type instead of just regular string.
Let’s say our style type can be only be bold or italic. We can create a union type by separating theme with a pipe. And now we can only assign this variable to this two specific values. And we’re not limited to just string, we could even extend this custom type with a number.
Interface
So that’s pretty cool but more often, you’ll be strong typing object that have multiple property with different multiple types. Let’s imagine we have two objects and we want to enforce that this object shape has a first and last name with string types.
Composing objects or class instances that don’t have the correct shape is an easy way to create bugs. But with TypeScript we can enforce the shape of an object with an interface. If we know the shape of object would be the same, then we can define an interface that defines the type of each property.
Now we can use this interface to strong type this object directly or we could use it as the return value from a function, argument, or anywhere else in our code.
Now sometimes an interface like this can be too restrictive. You can maintain the required properties and then add additional properties by creating a key with a type of string
with a value type of any. So now a first and last name will be required, but you also can add any additional property that you want to this object.
Function
Now let’s go ahead and switch to function. Strong typing in a function can be a little more complex because you have types for arguments and also the return value. Here we just have plain JavaScript function without any types.
Then we could add string values as the arguments and we wouldn’t any error from the compiler. Bu obviously this function is going to fail if we try to pass it any non-number value.
You can annotate arguments the same way we do with variables. That will sure only numbers can be passed to this function.
So the function implicitly has a number return value because we’re using the native math JavaScript library, but we can annotate a specific return value type after the parentheses and before the bracket. So if we set that type to a string you’ll see it’s underlined in red because it’s returning a number. To implement this function correctly we can call toString()
method.
In many cases you might have functions that don’t return a value or create some kind of side effect, in that case you can type your function return value to void
.
The next thing we’ll look at is how to strong type an array so we’ll start by creating an empty array then pushing a different values to it with different types.
We can force this array to only have number types by doing a number type followed by bracket, signifying that it’s an array. Now you can see we get an error every time to push a value that’s not a number.
This is especially useful when you’re working with an array of objects and you want to get some intellisense as you’re iterating over those objects.
TypeScript also opens the door to a new data structure called a tuple. Basically this is just a fixed length array where each item in that array has it’s own type.
You can each items values optional by putting a question mark after the type. You also can use this question mark syntax in other places.
Generic
The last thing is TypeScript generics. You may run into situation where you want to use type internally inside of a class or function. For example here I have Observable class that has an internal value that you can observe.
The T
represents a variable type that we can pass in to strong type this Observable internal value. This allows us to specify the internal type at some point in our code. For example we have an Observable of a number or person interface likes below. Or we can also do this implicitly if we create a new Observable of a number, it’s going to implicitly have that internal number type.
More often than not, you’ll be using generics rather than creating them. But it’s definitely important thing to know.
I’m gonna go ahead and wrap thing up there. Hopefully this article give you an idea of why TypeScript is so powerful but we really only scratched the surface here. Don’t forget to check the official documentation to learn more about TypeScript features.