DEV Community

Cover image for A Simple Introduction to TypeScript
Mac Little
Mac Little

Posted on • Updated on

A Simple Introduction to TypeScript

Just over a month ago, Stack Overflow released their annual Developer Survey.

Unsurprisingly, JavaScript is still the most commonly used programming language, making it eight years in a row.

And while my journey with JS is only beginning, let's say I wanted to learn another language. I could obviously just pick the second or third most popular language in the list, but popularity doesn't necessarily mean happiness, right?

Luckily, Stack Overflow's survey includes a Most Loved, Dreaded, and Wanted section which provides some interesting insights on which languages developers enjoy the most, enjoy the least, and are seeking to learn.

Today, we'll focus on the the second Most Loved language: TypeScript.

TypeScript and the Problem it Solves: Dynamic to Static

Developed by Microsoft and made public in 2012, TypeScript is considered a superset of JavaScript, which essentially means it is a powerful extension of JS.

TypeScript's main mission is simple: make JavaScript, which is a dynamically typed language, behave more like a statically typed language.

What's the difference between dynamic and static typing? Let's jump into an example:


var sayName = (person) => console.log(`Hello there ${person.name}!`)

const me = {
  name: 'mac'
};

sayName(me); // prints 'Hello there Mac!' to the console

In the above function, we create a function called sayName that just prints a welcome message to the console. But sayName is just a variable declared with var, so we can just change the value.

var sayName = (person) => `Hello there ${person.name}!`

const me = {
  name: 'mac'
};

sayName(me);

sayName = 55, // changing the value of sayName to a number

sayName(me);

And while we obviously would expect an error like this:

Alt Text

Why can't we at least see our first message, then see the Type Error? Because JavaScript is dynamically typed.

When we run a program, which consists of lines of code, that code is prepared to execute on whatever device or environment you've prepared, or what we call "compiled".

During this compiling phase or "compile time", statically typed languages like C# or C++ check the types and look for Type Errors before even running the program. If there is an error, the program will not run or even get to "runtime".

Conversely, dynamically typed languages like JavaScript go to runtime first then check for Type Errors. This can be particularly dangerous as programs expand in size, whether it be lines of code, files the code is distributed across, or both.

At least with our simple error above, we can easily re-trace our steps back to the re-defining of sayName a few lines above. In bigger applications, debugging is never that easy.

Thus, TypeScript was developed with the goal of bringing this "type check" to JavaScript before runtime to make our lives easier. So let's utilize TypeScript in a very simple way.

Implementing TypeScript

Let's borrow some code I wrote for my blog about destructuring, but with a few modifications. Here's what it looks like before we install and utilize TypeScript:

const slamDunk = ({first, last, points, team}) => {

  console.log(`${first} ${last} with the slam! The ${team} lead by ${points}!`);
}

const zion = {
  first: "Zion",
  last: "Williamson",
  team: "Pelicans",
  points: 2
};

slamDunk(zion);

This prints the following message the console:

Zion Williamson with the slam! The Pelicans lead by 2!

You'll notice that even through 2 is a number inside of our object, the template literals convert it to a string.

To re-factor this function using TypeScript, we have to do a few things. First, we need to install two dependencies: TypeScript and ts-node, which allows us to test our files directly in the terminal.

npm install -g typescript
npm install -g ts-node

Next, we need to create a TypeScript file using the .ts extension and then start writing our code. Here's our slamDunk.ts file in its' entirety.

Alt Text

Next, let's break down the key differences in this file from our plain JavaScript snippet above.

First, you'll notice that instead of going straight to our function, we have what is called an interface. An interface is a way for developers to explicitly declare what an object (in this case, a Player object) should contain, both for the keys, but also the types those values should be at the keys.

interface Player {
  first: string;
  last: string;
  team: string;
  points: number;
}

In the slamDunk function, we still have a player parameter, but again, we're explicitly stating that whatever inputs we take in must match the Player interface.

If the input matches our interface, we'll destructure it inside the function and then print our message.

const slamDunk = (player: Player) => {
  const {first, last, team, points} = player;
  console.log(`${first} ${last} with the slam! The ${team} leads by ${points}!`)
}

You'll notice that our zion variable remains unchanged. So if we run this command in the terminal:

ts-node slamDunk.ts

We'll get this message:

Zion Williamson with the slam! The Pelicans leads by 2!

This is all well and good, but this doesn't really display the power of TypeScript, so let's make some changes to our zion variable.

const zion = {
  first: "Zion",
  last: "Williamson",
  team: "Pelicans",
  points: "2" // changing the value to a string of 2
};

Our linter is already making noise, but let's try to run this in our terminal, just for kicks.

Alt Text

TSError: ⨯ Unable to compile TypeScript:
slamDunk.ts:21:10 - error TS2345: Argument of type '{ first: string; last: string; team: string; points: string; }' is not assignable to parameter of type 'Player'.
  Types of property 'points' are incompatible.
    Type 'string' is not assignable to type 'number'.

// this refers to the line of code where the error is
21 slamDunk(zion);

What this error tells us is that the key "points" is a string and not number, so our code can't be compiled as designed and thus won't be executed.

In our JavaScript example, this would still work. In fact, it might be preferred so we don't have to rely on the template literal changing the number to a string.

But with TypeScript, if it doesn't match the pre-determined type, it doesn't execute.

What if we tried to call slamDunk with an object that was missing one of our keys?

const lebron = {
  first: "LeBron",
  last: "James",
  team: "Lakers",
};

slamDunk(lebron);

We'd still get an error because "points" is actually missing this time.

TSError: ⨯ Unable to compile TypeScript:
slamDunk.ts:30:10 - error TS2345: Argument of type '{ first: string; last: string; team: string; }' is not assignable to parameter of type 'Player'.
  Property 'points' is missing in type '{ first: string; last: string; team: string; }' but required in type 'Player'.

30 slamDunk(lebron);

As we can see, while TypeScript is being compiled, it looks at all the variables and checks if it matches our rules. If it doesn't run. And better yet, the "errors" we get give us a specific reason the code can't compile: it doesn't follow our rules.

Conclusion

JavaScript is an incredible language and isn't going anywhere, but you probably didn't need me to tell you that. It's flexible, accommodating, and easy to learn, but like any system in technology, it comes with inherent trade-offs. Too much flexibility can lead to errors down the line, so utilizing TypeScript is a great way to control for any bugs that could potentially pop up.

And since TypeScript is just an extension of JavaScript, it's no different than pulling up the bumpers at the bowling alley. You can shoot down the middle all you want and one misstep won't send you in the gutter.

Top comments (0)