Type Script
TypeScript tutorial for beginners: what is TypeScript?
The definition from the official website says: "a typed superset of JavaScript" but it assumes you know what a "superset" is and what "typed" means. Instead to keep things simple you can think of TypeScript as of "a layer on top" of JavaScript.
TypeScript is a layer because you can write TypeScript code in your editor. After a compilation all that TypeScript stuff is gone and you're left with plain, simple JavaScript.
If the idea of a compilation step confuses you keep in mind that JavaScript is already compiled and then interpreted. There is a JavaScript engine that reads and executes your code.
But JavaScript engines are not able to read TypeScript code so any TypeScript file should go under a "pre-translation" process, called compilation. Only after the first compilation step you're left with pure JavaScript code, ready to run in a browser. You'll see later how the TypeScript compilation is done.
For now let's keep in mind that TypeScript is a special kind of JavaScript but it needs a "translator" before running in a browser.
TypeScript tutorial for beginners: why TypeScript?
At first you won't understand exactly why TypeScript makes sense, after all it gets stripped down before becoming JavaScript code. "What's the point of TypeScript" you'll ask. That's a good question my friend.
In reality you'll see its benefits as soon as it will catch serious and silly mistakes in your code. More important your codebase will become well structured and almost self-documenting. You'll also appreciate improved autocompletion in your editor but that's just a nice side effect.
Anyway, every now and then a new thread pops up on Twitter or on the "orange website" saying that TypeScript is useless (the TypeScript tax) or too awkward.
As with almost everything in IT there are partisans on both sides of the barricade. There are detractors and proponents for TypeScript but what matters is that TypeScript is a solid tool and having it in your tool belt won't make harm.
My goal here is to show the tool and help you forming your own idea on TypeScript.
TypeScript tutorial for beginners: setting up TypeScript
Setting up? Why so? Isn't TypeScript just a language? Kind of. TypeScript has also a binary which compiles TypeScript code to JavaScript code. Remember, browsers do not understand TypeScript. Let's install the binary then. Create a new Node project inside a new folder:
and then install TypeScript with:
Next up configure a Node script so we can run the TypeScript compiler easily:
tsc stands for TypeScript compiler and whenever the compiler runs it will look for a file named tsconfig.json in the project folder. Let's generate a configuration file for TypeScript with:
If everything goes well you'll get "message TS6071: Successfully created a tsconfig.json file." and you'll see the new file in the project folder. Now, keep calm. tsconfig.json is a scary configuration file. You don't need to know every single gist of it. In the next section you'll see just the relevant bits for getting started.
TypeScript tutorial for beginners: configuring the TypeScript compiler
It's a good idea to initialize a git repo and commit the original tsconfig.json before touching the file. We'll leave just some of the configuration options and remove everything else. Later you may want to compare your version with the original. For starting off open up tsconfig.json and replace all the original content with the following:
Save and close the file. First of all you may wonder what tsconfig.json is for. This configuration file is read by the TypeScript compiler and by any code editor with TypeScript support.
TypeScript compiles down to "vanilla" JavaScript. The key target determines the desired JavaScript version, ES5 (or a newest release).
Depending on the level of "strictness" for tsconfig.json the compiler and the editor will comply if you don't add the appropriate type annotations to your code (more on this in a minute).
With strict set to true TypeScript enforces the maximum level of type checks on your code enabling amongst the other:
noImplicitAny true: TypeScript complains when variables do not have a defined type
alwaysStrict true: strict mode is a safe mechanism for JavaScript which prevents accidental global variables, default "this" binding, and more. When "alwaysStrict" is set true TypeScript emits "use strict" at the very top of every JavaScript file.
There are a lot more configuration options available. With time you'll learn more, for now the two options above are everything you need to know for getting started. But what is "any" by the way?
A couple of words on "types"
By now you should've got an hint of what TypeScript does. Everything revolves around types. These are not the classic JavaScript "types" like String, Object, Boolean. TypeScript adds more type on its own like any (and more).
any
in particular is a "loose" TypeScript type. It means: this variable might be of any type: string, boolean, object, really, I don't care. Which in fact is like having no type checking at all. With strict set to true instead you say to TypeScript "don't allow ambiguity in my code".
For this reason I recommend keeping the max level of strictness on TypeScript, even if it can be harder to fix all errors at first. And now we're almost ready to see TypeScript in action!
TypeScript tutorial for beginners: TypeScript in action
Everything begins with a legitimate (apparently) JavaScript function: filterByTerm. Create a new file named filterByTerm.js in your project folder and copy the following code into it:
Don't worry if you don't understand the logic right now. Take a look at the parameters of that function and at how they are used a couple of lines later. Just by looking at the code you should have already spotted the problem (no it's not Java).
I'm wondering if there is a way to check that function in my IDE, without running the code or having to test it with Jest. Is that even possible? TypeScript is great for that, in fact it's one the best tool for static checking in JavaScript, that is, "testing" the correctness of your code before it even runs.
So take the leap into the TypeScript world and change the extension of your file from filterByTerm.js to filterByTerm.ts. With this change you're going to uncover a bunch of errors in your code:
Can you see those red marks under function parameters? From now on I'll show you errors in textual form, but keep in mind that IDEs and text editors display these red line whenever you make a mistake in TypeScript.
To confirm we're doing something wrong run:
and take a look at the errors:
Bingo! TypeScript is telling you that function parameters have the any
type, which if you recall can be any kind of type in TypeScript. We need to add the appropriate type annotations to our TypeScript code.
But wait, what's a type, really?
What are types and what's wrong with JavaScript?
JavaScript has types and if you worked with the language before you know there are strings, booleans, numbers, objects, and so on. As of today there are eight types in JavaScript:
String
Number
BigInt
Boolean
Null
Undefined
Object
Symbol
Everything in that list is a "primitive" except Object which is a type. Every JavaScript type has a corresponding representation which can be used in our code, like strings and numbers for example:
The "problem" with JavaScript is that a variable can change its type whenever it (or we) wants. A boolean for example can later become string (save the following code in a file named types.js):
The transformation can either be intentional, a developer might really want to assign "Tom" to aBoolean, but there are high chances that these kind of errors will happen by accident.
Now, technically speaking there's nothing wrong with JavaScript itself because its "type dynamism" is intentional. JavaScript was born as a simple scripting language for the web, not as a fully fledged enterprise language.
JavaScript relaxed nature however can pose serious problems in your code and undermine its maintainability. TypeScript aims to solve these problems by adding strong types to JavaScript. In fact if you change the extension of types.js to types.ts you'll see TypeScript complaining in the IDE.
The compilation of types.ts will produce:
Armed with this knowledge let's dig deeper into TypeScript types.
Dipping our toes into TypeScript types
TypeScript revolves around types and looks like our code has no types at all. Time to add some. We're going to fix function parameters first. By looking at how the function is called it seems it takes two strings as arguments:
Are we sure? Let's add your first type annotation to the function. Here's how:
That's it! By adding types to the parameters we're migrating our code from pure JavaScript to TypeScript. But if you try to compile the code:
here's what happens:
Can you see how TypeScript is guiding you? The problem is with the filter function:
We're telling TypeScript that "input" is a string but later in the code we call the filter method on it, which belongs to arrays. What we really want instead is marking "input" as an array of something, maybe an array of strings?
For doing so you have two options. Option 1 with string[]
:
or if you like this syntax, option 2 with Array<string>
:
Personally I prefer option 2. Now let's try to compile again (npm run tsc) and here it is:
TypeScript doesn't want to leave us alone I suppose. Don't blame it, we marked input as an array of strings and now we're trying to pass in a string. That's an easy fix! Let's pass an array of strings instead:
And here's the complete code so far:
Looks good to me. But if you compile it's not (npm run tsc):
Ok TypeScript, fair enough. We're passing in an array of strings but later in the code we try to access a property named "url":
That means we want an array of objects, not an array of strings. Let's fix that into the next section!
TypeScript tutorial for beginners: TypeScript objects and interfaces
We left with TypeScript complaining (what a surprise) because filterByTerm has been passed an array of strings. "url" property does not exists on type string TypeScript yelled. Let's help TypeScript then by passing an array of objects, where every object has the required url property:
and while you're there update the function signature so that it takes an array of objects:
Now let's compile the code:
and admire the output:
Here we go again! It makes sense, at least in TypeScript: the generic JavaScript object does not have any property named "url". And for me this is where TypeScript really starts to shine.
The point is, you can’t assign properties to a random object and call it a day. TypeScript expects that every entity in your code conforms to a particular shape. And that shape in TypeScript has a name: the interface.
Now, at first it will look like alien syntax, but once you get accustomed to interfaces you'll start to use them all over the place. But what's an interface by the way? An interface in TypeScript is like a contract. Or put it another way an interface is like a "model" for your entity.
By taking a look at our code we can think of a simple "model" named Link for an object whose shape should conform to the following pattern:
it must have a url property of type string
In TypeScript you would define that "model" with an interface, like so (put the following code at the top of filterByTerm.ts:
With the interface declaration we say "I want to use that shape in my TypeScript code from now on". That's not valid JavaScript syntax of course and it will be stripped away during compilation.
Now we can use our interface, which is actually also a custom TypeScript type, by fixing the parameter "input":
With this fix we say to TypeScript "expect an array of Link" as the input for that function. Here's the complete code:
At this point all the errors should go away and you can run:
The compilation step will produce a file named filterByTerm.js with plain JavaScript code in the project folder. You can check out the file and see how TypeScript specific declaration are stripped away.
Since "alwaysStrict" is set true the TypeScript compiler also emits "use strict" at the top of filterByTerm.js.
Great job on your first TypeScript code! In the next section we'll explore interfaces a bit more.
TypeScript tutorial for beginners: interfaces and fields
TypeScript interfaces are one of the most powerful construct of the language. Interfaces help in shaping "models" across your application so that any developer can pick that shape and conform to it when writing code.
So far we defined a simple interface, Link:
If you want to add more fields to the interface it's a matter of declaring them inside the block:
Now any object of type Link must "implement" the new fields, otherwise you get an error. In fact by compiling the code with:
TypeScript screams at you:
The problem is with the argument of our function:
TypeScript is able to deduct by looking at the function declaration that the argument is of type Array of Link. Thus any object inside that array must have (implement) all the fields defined in the interface Link.
Most of the time that's far from optimal. After all we don't know if every new object of type Link will ever have all the fields. Worry not, to make the compilation pass we can declare interface's fields optional with a question mark:
Now both the editor and the compiler will be fine. Yet TypeScript interfaces can do a lot more, in the next sections we'll see how to extend them. But first a brief note about variables in TypeScript.
TypeScript tutorial for beginners: typing variables
So far you've seen how to add types to function's parameters:
TypeScript is not limited to that, of course you can also add types to any variable. To illustrate the concept let's extract function's arguments one by one. First I'm going to extract every single object:
Notice how I can say to TypeScript that obj1, obj2 and obj3 are of type Link. In "vanilla" JavaScript you would write:
Next up we can define an array of Link like so:
And finally the search term:
Here's the complete code:
Ok, I feel you. TypeScript looks more verbose and sometimes redundant compared to JavaScript. But with time you'll see that the more you add types, the more your code becomes robust.
The more you help TypeScript to understand the intent of your code by adding type annotations, the more you'll be fine later. And your developer experience will skyrocket.
For example now that arrOfLinks is associate with the correct type (array of Link), your editor is able to infer that every object in the array has a property named url, as defined in the interface Link:
Now tell me this isn't fantastic because indeed it is. TypeScript has a lot more types besides string, Array and number.
There are booleans, tuples, "any", never, enums. With time you'll learn them all. If you're curious check out the documentation for the basic types.
Now let's move on to extending interfaces.
(Most of the times Typescript will be able to infer types on its own. As a rule of thumb let it do the job for you!)
TypeScript tutorial for beginners: extending interfaces
TypeScript interfaces are great. There will come a time however when you'll need a new entity in your code which happens to be almost the same as another existing interface. Let's say for example we want a new interface named TranslatedLink with the following properties:
id, number
url, string
description, string
language, string
Description, id, and url ... looks like we already have the Link interface with those very same properties:
Is there a way to reuse the interface Link? Turns out in TypeScript we can extend an interface by assigning its properties to a new interface, so that TranslatedLink for example "inherits" some traits from Link. Here's how to do it, notice the keyword extends:
Now any object of type TranslatedLink will have the optional properties description, id, url, and the new property "language":
When an object like link1 uses an interface we say that link1 implements the properties defined in that interface. The interface on the other hand has implementations when it's used for describing one or more objects in your code.
Extending an interface means borrowing its properties and widening them for code reuse. But wait, there's more! As you'll see soon TypeScript interfaces can also describe functions.
But first let's take a look at indexing!
TypeScript tutorial for beginners: the indexing interlude
JavaScript objects are containers for key/value pairs. Imagine a simple object:
We can access the value of any key with the dot syntax:
or with the bracket syntax (the same is true for JavaScript arrays since arrays are a special kind of object):
Now imagine that the key becomes dynamic, so that we can put it in a variable and reference it inside brackets:
Now let's add another object, put both inside an array, and filter the array with the filter method like we did in filterByTerm.js. But this time the key is passed dynamically so becomes possible to filter by any key:
Here's the relevant line:
Will it work? Yes because JavaScript does not care whether paolo or tom are "indexable" with a dynamic key. And what about TypeScript? Will it give an error in this case?
Let's find out: in the next section we'll make filterByTerm more dynamic with a variable key.
Interfaces can have indexes
Let's get back to filterByTerm.ts and in particular to our filterByTerm function:
It does not look so flexible because for every Link we match the hardcoded property "url" against a regular expression. We may want to make the property, thus the key, dynamic. Here's a first try:
lookupKey is the dynamic key, which gets also assigned a default parameter as a fallback, the string "url". Let's compile the code:
Does it compile?
Here's the offending line:
"No index signature". Wow. It's an "easy" fix. Head over the interface Link and add the index:
The syntax is kind of weird but is similar to the dynamic key access on our objects. It means we can access any key of that object through an index of type string, which in turn returns another string.
Anyway, that first try will make other errors pop up like:
That's because some properties on the interface are optional, maybe undefined, and not always of type string (id is a number for example).
We can try to fix the problem with a union type, a TypeScript syntax for defining types that are the union between two or more other types:
The following line:
means that index is a string and may return another string, a number, or undefined. Try to compile again and here's another error:
Makes sense. The match method works only for strings and there's a chance that our index will return a number. To fix the error we can use any
as a workaround:
The TypeScript compiler is happy again, but at what cost?
Now it's time to turn our attention to another fundamental TypeScript feature: return types for functions.
TypeScript tutorial for beginners: return types for functions
It's been a lot of new stuff up until now. Anyway, I skipped over another TypeScript's useful feature: return types for functions.
To understand why it's handy to add a type annotation for return values imagine me, messing with your fancy function. Here's the original version:
If called as it is, passing in the array of Link you saw earlier and the search term "string3", it should return an array of objects, as expected:
But now consider an altered variant:
If called now, with the same array of Link and the search term "string3", it returns ... [object Object]`!:
Can you spot the problem? Hint: toString
.
The function is not working as expected and you would never know unless hitting production (or testing your code). Luckily TypeScript can catch these errors, as you write in the editor.
Here's the fix (just the relevant portion):
How it works?By adding type annotations before the function's body we tell TypeScript to expect another Array as the return value. Now the bug can be spotted easily. Here's the code so far (altered version):
Now compile it:
and check out the error:
Fantastic. We're expecting an array of links, not a string. To fix the error remove .toString()
from the end of filter and compile the code again. It should work now!
We added another layer of protection to our code. Granted, that bug could have been spotted with a unit test. TypeScript is a nice layer of safety rather than a complete replacement for testing.
Let's continue the exploration with type aliases!
TypeScript tutorial for beginners: type aliases vs interfaces
So far we've seen the interface as a tool for describing objects and custom types. But lurking through other's people code you might also have noticed the keyword type
.
Apparently interface
and type
are used interchangeably in TypeScript, but they're different in many ways. And that's cause of confusion for TypeScript beginners.
Remember: an interface in TypeScript is the shape of something, most of the times a complex object.
A type on the other hand might also be used to describe a custom shape, but it's just an alias, or put it another way, a label for a custom type. For example, let's imagine an interface with a couple of fields, one of them being a union type of boolean, number, and string:
With a type alias you can extract that custom union type for example, and create a label named Authenticated:
This way you can isolate what changes, so you don't have to copy/paste the union type all over the codebase.
If you want to apply type to our example (filterByTerm) create a new label named Links and assign Array to it. That way you can reference the type:
Now, that's not the most clever example of labelling types but you should get the point. So what to use between interface and type? I prefer interface for complex objects. An approach suggested by the TypeScript doc as well:
Because an ideal property of software is being open to extension, you should always use an interface over a type alias if possible.
Hope it helped to clarify your doubts.
In the next section we'll take a quick look at two more TypeScript topics before saying goodbye. Keep going!
TypeScript tutorial for beginners: more on interfaces and objects
Functions are first class citizens in JavaScript, yet Object is the most important entity in the language.
Objects are mostly containers for key/value pairs and it should be no surprise that they can also hold functions. When a function lives inside an object it has access to the "host" object through the keyword this
:
So far you saw TypeScript interfaces applied to simple objects for describing strings and numbers. But they can do a lot more. Let's make an example. Create a new file named interfaces-functions.ts with the following code:
It's a JavaScript object but it needs types. Let's make an interface to make tom conforming to a well defined shape. How about "IPerson"? And while there let's also apply the new interface to tom:
Compile the code (npm run tsc) and watch it fail:
Cool, IPerson does not have any property named printDetails but more important it should be a function. Luckily TypeScript interfaces can also describe functions. Here's how:
Here we added a property printDetails of type function, returning void. void is useful as a return value for functions that ... don't return anything at all.
A function that prints to console in fact does not return anything. If we were to return a string from printDetails we could adjust the return type to string:
Now, what if the function has parameters? In the interface you can add type annotations for them:
and if you start typing "an..." inside an object implementing IPerson, most IDE will auto complete the function for you. Developer productivity at its finest.
Last updated
Was this helpful?