Typescript Fundamental Concepts [Part 1]

Typescript Fundamental Concepts [Part 1]

Hey there! Are you ready for your next adventure? I shared some insights on a design system I created a while ago, and I got so many great comments and thoughts. It motivated me to write more articles about tech, specifically ones where I can share my knowledge and teach you something new.

With that in mind, I have created this comprehensive guide divided in 3 Parts about Typescript to take you from the basics to mastery of this powerful language.

Just to let you know, in case you don’t know, Typescript is a popular programming language that is a superset of JavaScript. It adds optional static typing and other features to the flexible JS language. If you're unfamiliar with TypeScript, you are in the right place. This language can help you improve the quality and maintainability of your code and build larger-scale applications.

In this post, I'll cover the basics of TypeScript, including its syntax, type system, and tooling support. Plus, I'll provide easy-to-follow examples to help you get started with this amazing language and see how it can help you write cleaner, more reliable, and more scalable code. Whether you're a seasoned JS developer looking to upgrade your skills or a beginner interested in learning a powerful new language, TypeScript has much to offer. So buckle up and get ready to learn all about TypeScript - the programming language that's taking the JavaScript community to the next level!

NOTE: This article actually assumes that you are already familiar with JavaScript concepts and syntax. If you do not have this background knowledge, it is essential that you first learn JavaScript before reading this article. While it may still be possible to follow along without JavaScript knowledge, a full understanding of this article will require additional resources or additional study.

With that being said, let's start learning. I'll cover the following topics in the next three articles I'll write:

Part 1:

  • Basic Setup for Front-end or Back-end app
  • Basics & Types
  • Advanced Types [1]
  • Type Guards [1]

Part 2:

  • Classes
  • Interfaces
  • Functions
  • Mixins
  • Modules
  • Type Guards [Part 2]
  • Advanced Types [Part 2]


Basic setup for Front-end or Back-end development

How to start using Typescript

Before we can start using TypeScript, we must ensure that Node.js and npm are installed on our computers. To install Node.js and npm on your computer. You can download the latest version of Node.js from the official website (https://nodejs.org/) and install it using the provided installer. npm is included with Node.js, so you don't need to install it separately.

Starting with a Front-end app with React + Typescript

In order to start developing a front-end app, we need to run the following command in our terminal (Remember to change my-app-name for the name of your project):

No alt text provided for this image
Picture 1: Command for starting a new React with Typescript project.

Using the create-react-app tool with the --template typescript flag will create a new React app with a TypeScript template, allowing you to start building your app immediately without setting up TypeScript manually.

Starting with a Back-end app with Node.js + Express + Typescript

On the other hand, if we want to create a back-end app using Node.js, Express, and Typescript, we could use the following command in our terminal to start immediately.

No alt text provided for this image
Picture 2: Command for starting a new Node.js, Express.js with Typescript project.

I want to let you know that using these tools to create an app will add some boilerplate code that you might not want or need in your application. If you want more control over the setup and structure of your app, I personally recommend you to create the project from scratch and set it up manually. This allows you to choose the specific dependencies and configure the TypeScript compiler to fit the specific needs of your project. While it may take a bit more time to set up the project manually, it can result in a more tailored and efficient solution in the long run.

NOTE: So that you know, some of the examples in this article will be based on a front-end application. While the concepts and techniques discussed in this article can also be applied to a backend application, the specific examples may not directly translate. If you wish to follow along with the examples in this article, I recommend you work with the front-end application. That being said, with our working environment set up, we can now delve into the fundamentals of TypeScript.


Basics & Types

Variables

Let's say that we are declaring some variables in javascript, it is well known that it's pretty simple to declare a variable, we simply write the keyword var, let or const; followed by the name of the variable or constant, and we can optionally assign a value to it at the same time (note that it's mandatory for const constants to be initialized with a value). In javascript we typically would declare our variables this way:

No alt text provided for this image
Picture 3: How variables are declared in javascript.

How can we use Typescript in these variables? It's actually quite simple – to use Typescript with variables, functions, and other values, we need to specify the type by adding a colon and the type name after the variable's name. For example, we might declare a variable like this: let myVariable: string = 'hello'; this tells Typescript that myVariable is a string.

No alt text provided for this image
Picture 4: How variables are declared in Typescript

This is how we can use Typescript very simply to improve our Javascript code. Please note that we also need to rename our files from .js or .jsx to .ts or .tsx and use TSLint (Deprecated) or ESLint extensions in our code editor to get syntax highlighting, type checking, and suggestion features.

Now that we have our variables well-typed, if we try to assign a value of a different type than the one we defined, we can now catch errors immediately while writing our code thanks to the syntax highlighting provided by our code editor and extensions. Let's check the following example for a better understanding:

We will assign the string value "30" to the age variable of type number to see what happens:

No alt text provided for this image
Picture 5: Type the error in the variable age listed by the code editor.

As you can see, our code editor is flagging a type error in the variable age. This is because we declared age as a number, but we are attempting to assign a string to it. Our code editor is warning us that Type 'string' is not assignable to type 'number'. This is the main benefit of using typescript because it helps us catch errors before our code is even compiled or run.

There are many types we can use in our variables. Let's take a look the next code just to see more examples:

No alt text provided for this image
Picture 6: Different variables declaration with types.

Wow, Rafa! Wait a moment please. What just happened? - Don't worry, I'll try to explain it in detail. The first example was just the introduction of the use of TypeScript. As you can see now, there are more types to learn and more ways to declare types.

If we just ignore the details in the code above, what we can see in the big picture is that they are still following the same syntax: variable: type, but what are all those new stuff that I put in the code above? let's dive deep into types...


Objects

Let's start with the object person, in order to define the types of its properties, we can use the following syntax: const person: { name: string, lastname: string, age: number }, this is like creating a new object but instead of assigning values we are defining types. By doing this, we can ensure that the object is well-typed.

Another thing to mention here, is that we also can define those types separately from our declared variables by using the type alias keyword like follow:

No alt text provided for this image
Picture 7: How to declare an Object Type using Type Aliases.

Then we can just use that type reference in order to define the type for our person object. Another benefit of using type aliases is that we can reuse the same type definition in multiple places in our code. This helps to reduce code duplication and makes it easier to ensure that our variables have consistent types throughout our application. For example:

No alt text provided for this image
Picture 8: how a Type Alias (Person) can be used in multiples objects instances.

So by this way we've used the type alias Person to define the types for instances person1 and person2, optimizing our code and reducing code duplication.


Arrays

In the Picture 6, we have declared a variable called categories which is an array of strings. The type of categories is indicated as string[], which means that it is an array of elements that are all of the type string.

The square brackets [] are used to denote that a variable is an array in TypeScript. The type string is appended to the brackets to specify that the elements of the array will be of that specific type. This type syntax ensures that the categories array can only hold elements that are of type string.

Declaring the type of an array in this way is important because it allows the linter to catch type errors. This can help prevent bugs and make it easier to maintain and debug our code.

To better illustrate this point, consider the following example, we are trying to assign the value 2000 (number) to the first element in the categories array. However, the linter now is raising a type error because we are attempting to assign a value of type number (2000) to an array that is intended to hold string values.

No alt text provided for this image
Picture 9: Type error in categories array of strings

In Picture 6, we also see the declaration of an array called oddNumbers which uses the syntax Array<Type> to define its type. This syntax is equivalent to using Type[], which is a shorthand for the syntax Array<Type>. Both of these syntaxes can be used to define the type of an array in TypeScript.

In addition to using the type[] or Array<type> syntax, we can also declare the type of an array using the type keyword. This allows us to define the type of an array separately from the variables that use it, just like we did it in Figure 7 for objects types.

Now you should be able to understand the Figure 6 entirely, you've learnt how to declare types for primitives (strings, numbers, booleans), and for objects and arrays.

PD: The type Array<Type> is using the Generic Type syntax which we are going to explain further in Part 3.


Type Any

The any type in TypeScript acts like a wildcard, allowing variables to be assigned any value without a type check. While this can be useful in certain situations where the type of a variable is not known, it is generally not recommended to use the any type. This is because using any effectively removes the type checking that is one of the main benefits of using a typed language like TypeScript. It is important to use the any type wisely and only when absolutely necessary. Let's see an example.

No alt text provided for this image
Picture 10: An example of using "any" as type

In the example above we see the getPersonAge function, which takes a person: any as a parameter. The parameter is declared with the any type, meaning that it can be assigned any value without a type check. When we use any, we risk encountering problems at runtime due to incorrect data types, so for that reason as I already mentioned before, it is generally not recommended to use the any type . If the previous example was not clear, let's check this another example:

No alt text provided for this image
Picture 11: Passing an string to the function getPersonAge

We have the same code, but in this case, we are passing person as a string. The linter will not raise a type error, and we will only notice the issue when we execute this code. For that reason, my recommendation to avoid these issues and take advantage of the benefits of type checking is to specify the types in our code whenever possible. The following code demonstrates how we should use the types correctly for this particular case.


No alt text provided for this image
Picture 12: Assigning an specific type for the parameter "person"

Advanced Types

Ok so now we are going to explore some more advanced concepts that we can find in Typescript, and these are:

  • Unions and Intersection Types
  • Type Guards

Understanding these concepts can help us to write better Typescript code and have more consistent code in our apps.

Union Types

We sometimes will need to have types that can be one or another type. For example, we can create a type Vehicle that accepts either a Bicycle or an Electric Scooter, in this case, we should use the operator | (pipe) to indicate that type will accept one or another type. This is commonly called Union Type, and works like follow (look at line 19 in the picture below).


No alt text provided for this image
Picture 13: Union Types with objects values

Union types allow us to create objects that can be either of two or more different types. In the case of our Vehicle type, we used the pipe operator to specify that it can be either a Bicycle or an ElectricScooter. This allows us to create objects like myBicycle and myScooter with different properties and values, but still have them be recognized as instances of the Vehicle type. As you can see in Picture 13, we were able to create these two objects using the Vehicle type without any issues. This is because the type system automatically recognizes that the object is either a Bicycle or an ElectricScooter, depending on the specific properties and values it has.

Intersection Types

Intersection types allow us to combine multiple types into a single type. They are a little bit different from Union Types since we don't want to pick one from between two or more types; we are just combining them. And also, we need to use the ampersand (&) instead of the pipe (|) symbol. Let's check the following code as an example:


No alt text provided for this image
Picture 15: Intersection Types

In this example, we have two independent types called Color and Space, which could be used in different parts of our code. We also want to create a new type called Styles that combines all of the properties of both of these types. To do this, we can use & operator to create the Intersection Type.

The Styles type will have all of the properties of both the Color and Space types, allowing us to use it to create objects like myStyles that have all of these properties combined. This can be useful when we want to reuse the properties of these types in a single object or function.

Note that for this specific example we need to put all properties to our object myStyles as defined in our types, because they were defined as mandatory types, otherwise way we are going to get an error like this:


No alt text provided for this image
Picture 16: Error message when a property is missing

There are two errors here. The first one is that the type '{ color: string; padding: string; }' is not assignable to the type Styles. This is because Typescript is validating the entire object against our Styles type. The object we are trying to assign to myStyles does not match the Styles type basically because they have different structure.

The second error is that the property margin is missing in the type '{ color: string; padding: string; }', but it is required in the type Space. This error message is telling us that we have a missing property in the assignment we are trying to make. Because Styles is composed of Color and Space types. To fix this issue, we have two options: we can add the margin property and value to the myStyles object, as we did in Picture 15, or we can make it optional by adding a question mark after the property name, for example:


No alt text provided for this image
Picture 17: Optional properties

As you can see on line 7 on Picture 17, we added a question mark to make the margin property optional. By doing this, we are no longer getting an error due to the missing property. The optional feature can be used for properties and parameters.


Type Guards

This is a technique in where we identify the type of a variable in a conditional block. They are useful in cases we have to handle multiple types (for example when we are using Union Types) and we need to to apply some conditions to be sure that we are getting the specific types we want. Let's say we have the following code:

No alt text provided for this image
Picture 18: Function that get a price and add a dollar sign.

As you can see in Picture 18, we have the function formatPrice. This function receives a price that can be either a string or number and then returns a string that corresponds to the price formatted with a dollar sign. One thing to note is that when we examine the String.prototype.concat() method, we'll notice that this function only receives strings. But we could pass to it a number, right? well, typescript understands this, and then it's showing us the following error:

No alt text provided for this image
Picture 19: Typescript telling us a type error in the function formatPrice

What typescript is telling us here, is that we are trying to use the parameter price which could be a number, in the function concat() that only receives strings. Well, fortunately, for those parameters that are not specifically strings, concat() will automatically convert them into strings.

So, in this case, the function will still work well, and it will return the expected result without issues. If we would use Javascript instead of typescript, we could use the code as is, because the number parameters (or variables) will automatically be parsed to a string. This mechanism of concat() is called String Coercion. So, it doesn't matter what type of values we pass to concat(), it will always convert them into strings.

But we need to keep in mind that since this transformation is not controlled by us, and it occurs automatically, it could tend to produce unexpected runtime errors in our apps, we have to be carefully with this kind of things that usually happen ?under the hood?, and because we are using typescript, we are still having the type error in our editor, right?

Well, for that reason is a better approach to implement Type Guards in our code in order to control these situations and ensure that our applications will not get broken or get unexpected issues when are running.

In order to implement our Type Guards, we can use the following:

  • Typeof
  • In


Typeof operator

Typeof is a javascript operator that we can use to get the type information of any value in runtime. Typescript usually expects from typeof the following types:

  • string
  • number
  • bigint
  • boolean
  • symbol
  • undefined
  • object
  • function

We can use the typeof operator to validate the data types of our variables and parameters before executing certain logic in our code. TypeScript automatically recognizes this syntax and performs the necessary checks.

Let’s see the following example:

No alt text provided for this image
Picture 20: Function getPriceWithTaxes using type guards in a conditional statement

As you can see, we are validating the type of the price variable. Depending on its type, we perform different actions. If the price is a number, we perform the calculation and return the result. If the price is a string, we convert it to a number before performing the tax calculation. Finally, if it is neither a string nor a number, we return a warning message. And there you have it! our code is now more robust and resistant to errors.


In operator

That’s ok for primitive values. However, what do we do when working with objects? Thankfully, we can use the ?in? operator (along with ?typeof? operator as well if we want) to check their properties (or types). For example, consider the myScooter and myBicycle objects defined in Picture 13. Let's say we now have a function called ?getBatteryCapacity? declared inside our myScooter object, like follow:


No alt text provided for this image
Picture 21: Redefinition of myScooter object by adding a method called ?getBatteryCapacity?.

If we want to call this method, but we are not sure what kind of vehicle we are receiving from parameters, we can use the ?in? operator in order to validate the existence of any property or method within an object, like for example:

No alt text provided for this image
Picture 22: Validating methods of an object by using the ?in? operator.

In the above example, as you can notice, we are utilizing the ?in? operator to validate the presence of a method within the object ?myScooter? which is a type of Vehicle, since a Vehicle can also be a Bicycle (or any other kind of vehicles we could define as Vehicle), we need to perform this validation and by doing so, we can be sure that the method exists before calling it, making our code more robust and less prone to errors.

Please note that if you want to try this code by yourself, don’t forget to update the ElectricScooter type declaration (Picture 13) by adding the method getBatteryCapacity like below:

No alt text provided for this image
Picture 23: ElectricScooter's type updated with the new method.

Congratulations for completing this guide!

This was just the first step in your journey with Typescript, you've now learned about basic types declaration, setting up a project, advanced types using objects and functions, and also some tips on using type guards to prevent unexpected issues in our code. Keep up the good work!

Remember that Typescript is a powerful language that offers many beneficial features for development, making our apps more robust and scalable. While it may be challenging to fully grasp the language at first, I am confident you will love it.

As English is not my native language, creating this article was a bit of a challenge for me. Before moving on to Part 2, I will be reorganizing the content and coming up with new ways to explain the concepts.

In Part 2, I will delve into topics such as Classes, Interfaces, Mixins, Modules, Type Guards [2] and Advanced Types [2]. I would greatly appreciate any feedback on this guide before I begin work on Part 2. Please feel free to share any advice or insights that could help me improve my next article.

Don't forget to leave a comment or share this guide with your friends.

If you are thinking to stay updated on my latest articles, I highly recommend subscribing to my newsletter on Learning Software Engineering. Trust me, this is only the beginning, there's so much more exciting content coming your way throughout the year!

See you soon!

Rafael Rojas Covarrubias

David Antunes

Cloud Engineer Senior at Globant | Google Cloud Certified | AWS Certified

2 年

Thanks for sharing Rafa!

回复
Nicolás Peredo Esquivel

Lead Software Engineer at Globant | Creating digital experiences | Photographer in my free time

2 年

Great article Rafa ! Looking forward to the next one ! ??

Cristian Azócar

Ingeniero de Software en Xero

2 年

Good job Rafa! ?? Very insightful article. Looking forward to the next one ??

要查看或添加评论,请登录

Rafael R.的更多文章

  • Typescript Fundamental Concepts [Part 2]

    Typescript Fundamental Concepts [Part 2]

    Hey there! Welcome to Part 2 of this TypeScript Fundamental Concepts course. If you still need to read the previous…

    1 条评论
  • My First Design System In Figma

    My First Design System In Figma

    Hello! My name is Rafael, and this is my first post ever. I'm a graduated software developer since 2016 but I started…

    8 条评论

社区洞察

其他会员也浏览了