TypeScript Introduction - The Basics
Tags: javascript, typescript, webdev, tutorial
Published: 2019-03-01 16:09:26

**This post is taken from [my blog](https://areknawo.com), so be sure to check it out for more up-to-date content 😉**

If you have read some of my previous posts or any kind of JS blog for that matter, chances are you've heard about  **[TypeScript](http://typescriptlang.org/)**. For those who haven't -  **TS**  (TypeScript official abbreviation) is nothing more than  **modern JavaScript**  with the addition of  **static type system**. And, it's gaining a lot of attention recently. That's mainly because of the advantages it provides over standard, dynamically-typed JS. So, in this article, I'll guide you in making your first steps with TypeScript assuming you already know JavaScript. It's going to be an  **in-depth series**  covering almost everything from pure basics to some complexities. I'll try to explain it all as simple as I possibly can (pros out there - please don't accuse me of oversimplifying stuff 🙃).  **Enjoy!**  😉

![forest trees marked with question marks](https://images.unsplash.com/photo-1458419948946-19fb2cc296af?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ)

# Why (not)?

Before we proceed to learn TS, let's first explore why it's  **worth your effort**.

To start, TypeScript is a  **compiled language**  that's default compilation target is JavaScript. It's an  **open source**  project started and overseen by  **Microsoft**. It provides built-in support for modern  **ES-Next features**  and  **static type system**. And, while great many of day-to-day JS developers use e.g. Babel to utilize ES-Next, a concept of static typing might be something new for you (unless you've interacted with other statically-typed languages before 😁).

Static typing makes types of variables known at the compile time. JS is an interpreted or JIT-compiled language with a dynamic type system. But, what's more, important for you like the programming language end user (aka consumer) is what it translates to in real-world use. In this case, static typing brings better error-proneness, and usually much finer  **IDE support**  and  **tooling**. Thus, it can greatly improve your  **coding experience**. As for the dynamic type system, it has its advantages too. The main one being not having to specify your types in your code directly. 😅

There are not much more reasons beyond the mentioned static type system, that would make any kind of big difference. So, if you'd like to learn TS and improve both your knowledge and development experience, follow me and discover the hidden potential of  **static typing**.

# Basics

Every feature provided in TS is  **non-invading**, meaning that it has syntax that doesn't overlap with any part of the JS code. This makes porting your JS app back and forth relatively easily.

You can specify your variable's type using the colon (`:`) followed by the actual name of the type:

```typescript
const myStringVariable: string = "str";
```

There are 6 basic,  **primitive types**  to remember:

-   **number**  - represents any kind of  **numeric**  value - integer or float, hex, decimal, binary etc.
-   **string**  - represents any kind of  **string**  value;
-   **boolean**  - represents any  **boolean**  value, i.e.  `true`  or  `false`;
-   **symbol**  - represents  `symbol`  values;
-   **null**  - represents  `null`  value only;
-   **undefined**  - represents  `undefined`  value only;

This shouldn't be anything new for you. Each one of the above types is properly documented as a part of JS language itself. This fact is only a little bit hidden because of JavaScript's dynamic type system. But, rest assured, TypeScript has much more to offer than just that. And I think we should dig deeper! 🌟

# More types!

## Object

Starting the list of more complex types, we have the  `object`  type. The thing to remember is that it represents  **any non-primitive value**. Which means that primitives are not assignable. That's because almost everything in JS is an object. As you can see, TypeScript greatly respects JavaScript architecture. 😀

```typescript
const myObjectVariable: object = "str"; // error
const myObjectVariable2: object = {};
```

## Any

`Any`  type, as the name suggests, indicates any possible value. It serves as a kind-of fallback, allowing you to  **omit type checking**. It's really helpful at the beginning when porting from JS. But, it shouldn't be overused, or even better - it shouldn't be used at all! You don't use TypeScript to just type  `: any`  everywhere, do you? 😂

```typescript
let myAnyVariable: any = "str";
myAnyVariable = 10;
myAnyVariable = true;
```

## Void

`Void`  type, with its sounding name, represents the complete absence of type. This is commonly used with functions and tells the compiler that function doesn't return anything. Anything, in this case, includes  `undefined`  and  `null`, but who cares? Both of them seemed  _voided_  anyway. 😅 For your information, you most likely won't use this type with variables, but have a look at how strange it might be feel like:

```typescript
let myVoidVariable: void = undefined;
myVoidVariable = null;
```

## Never

`Never`  type, according to the pure definition, represents the value that  **never occurs**. But what exactly does it mean? Basically, it refers to e.g. return type of the function that throw/returns an error, which doesn't allow the function to have a reachable end point. It's also used with, so-called  **type guards**  (more about it later). Generally speaking, this type isn't used very often. Anyway, examples will come later, together with some more advanced stuff.

## Unknown

Unknown is a relatively new addition to TS types collection - it was introduced in  _v3.0_. It's meant to be a type-safe alternative to  `any`  type. How would something like that work? Well, first, any value can be assigned to unknown, just like with  `any`:

```typescript
const myUnknownVariable: unknown = "str";
const myAnyVariable: any = "str";
```

The difference appears when it comes to assigning the variable of  `unknown`  type to anything else. Here's what I mean:

```typescript
let myUnknownVariable2: unknown = myUnknownVariable;
myUnknownVariable2 = myAnyVariable;

let myStringVariable: string = "str";
myStringVariable = myAnyVariable;
myStringVariable = myUnknownVariable; // error
```

Unknown is not assignable to anything but itself...

That's what  **official TS documentation**  says and what generally clarifies the difference between any and unknown.

![black framed panto-style eyeglasses beside black ballpoint pen](https://images.unsplash.com/photo-1453906971074-ce568cccbc63?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ)

# Composition types

By that moment, we've discovered  **TS primitive**  and  **top types**  ( that's how these built-in types covered in the above section are called). Now it's time to explore some even more interesting types. Ones that don't always have their direct JS counterparts. I call them  **composition types**because they're composed of some smaller parts. Just to be clear - this name isn't official in any way. 😁

## Unions

In a nutshell,  **unions**  allow you to specify variable's type that you can assign  **different types**  of values to. They function as a list of possible and assignable types. They can be specified by writing your types, divided by the pipe symbol (`|`).

```typescript
let myUnionVariable: string | number = "str";
myUnionVariable = 10;
myUnionVariable = false; // error
```

Union types have incredible potential. You can use them to handle e.g. different types of parameters in functions or replace your  `any`  types with these, truly  **type-safe**  alternatives.

## Literals

**Literal**  types allow you to strictly define the possible value for the variable. Literals themselves aren't any kind of composition types, but they're so often used with e.g. unions and others, that I put them in this category. Now, how a literal type looks like? Well, just like a possible value it annotates:

```typescript
let myStringLiteral: "str" = "str";
let myNumberLiteral: 10 = 10;
let myBooleanLiteral: true = true;

myStringLiteral = "string"; // error
myNumberLiteral = 1; // error
myBooleanLiteral = false // error
```

I think that with the example above, you can easily understand the idea behind literal types and that you can imagine just how well these integrate with e.g. unions:

```typescript
let myVariable: "on" | "off" = "off";
myVariable = "on";
myVariable = "suspend" // error
```

But what if you want to literally (that's a good word here 🙃) express some more complex value like an object? Well, you do exactly the same thing:

```typescript
let myObjectLiteral: {str: string} = {str: "str"};
myObjectLiteral.str = "string";
myObrjectLiteral.num = 10; // error
```

## Intersection types

**Intersection**  types are closely related to union types. While union types function like logical or, intersection types function  **like logical and**. Thus, you can create them using the  **and**  sign (`&`).

```typescript
const myIntersectionVariable: {str: string} & {num: number} = {
	str : "str",
	num: 10
};
```

The created type has properties of all operands. These types are often used with object literals and other complex types and techniques which we'll cover later.

## Arrays

After all these types it's time to meet good old  **arrays**. Here, I'll introduce you to the first way of typing an array value. That's because there are two methods of achieving the same goal - more on that later. For now, to denote an array type, you have to write the type for actual values of your array and proceed it by the square brackets symbol (`[]`).

```typescript
const myStringArrayVariable: string[] = ["str", "str"]; 
```

Just to remind you - you can join and  **use together**  many of previously met types. You can e.g. create a type for an array of strings and numbers with union types, or create a type for an array of literal values. The options are endless! 🤯

```typescript
const myUnionArrayVariable: (string | number)[] = ["str", 10];
const myLiteralArrayVariable: ("str")[] = ["str","str"];
```

I guess that by that moment you already know that in TS additional spaces don't matter. Also, note the round brackets (`()`) in the above snippet. Just like in normal math (and also JS), they're used to  **group stuff together**. Seems pretty logical. 😅

## Tuples

Structures closely related to arrays, so-called  **tuples**  can be utilized to specify a type of an array with a  **fixed number of elements**, with all of them having  **strictly specified type**. Take a look at the example:

```typescript
const myTupleVariable: [number, string] = [10, "str"];
const myTupleVariable2: [string, number] = [10, "str"]; // error
```

It explains mostly everything. To define a tuple type, you start with square brackets (`[]`) that are really characteristic for arrays of any kind, any include types for your tuple one by one, separated by commas. Again, pretty rationale stuff.

## Enums

**Enums**  can feel somewhat new to some JS programmers out there. But in truth, these are commonly known among statically programming languages communities. They are used to simply provide more  **friendly names**  to  **numeric values**. For example, there's a common pattern for requiring different numbers in configuration objects or etc. That's where enums find their use-cases.

Enums are defined in a bit different way than any types we've met before. Namely, by using  `enum`keyword.

```typescript
enum Color {Red, Green, Blue};
```

In the example above we've defined an enum named  `Color`  with 3 members -  `Red`,  `Green`,  `Blue`. By default, each these members start numbering from 0,  **increasing by 1**  with every next entry. With that said, by using enums you can access both its member numeric value and name too! Let me show you what I mean. 😁

```typescript
Color.Red // 0
Color.Blue // 2
Color[1] // "Green"
Color[2] // "Blue"
```

As you can see, you can easily use enums for standard values. But we're in TS and we're all about types here, so how to use enums as types? Well, easy enough - just like other types!

```typescript
let myColorEnumVariable: Color = Color.Red;
myColorEnumVariable = 2;
myColorEnumVariable = Color[1]; // error
```

So, a variable of an enum type can be actually taken as a union of number literals, I think. You can assign to it an appropriate number or value of enum member. No other values are allowed even member's names.

Now, a quick note about enums' members numbering. As I said, by default it starts from 0 and increases by one every next member. But you can actually change that by assigning the value directly.

```typescript
enum Color {Red, Green = 32, Blue};
Color.Red // 0
Color.Green // 32
Color.Blue // 33
```

And, in the example above, we've overridden the value of  `Green`  member. In this way, the  `Red`value stays the same - 0 as by default,  `Green`  is assigned a value of 32, and  `Blue`  is 33 due to the rule of increasing by 1.

To summarize, enums are pretty useful when used properly and IMHO, they have one of the hardest or rather newest syntax for JS users to remember. But it will be very helpful when we'll be talking about interfaces, so let's move on! ⚡

# Functions

After learning all the types and stuff above, I think it's time to finally get to know how to properly  **type functions**! With this knowledge, you should be able to start writing some TS code for real!

Typing a function is similar to other TS code we've written before. We still have the colons and common type-name syntax but in a different place.

```typescript
function myFunction(myStringArg: string, myNumberArg: number): void
{
	// code
}
```

As you can see, the arguments section of the function is followed by our standard type annotation. It informs the compiler about function's  **return value type**. In the example above it's  `void`. I mentioned earlier when talking about this special type, that it, in fact, indicates the absence of any type at all. This means that our function above doesn't return anything. Simple, right?

Naturally, there's more to typing a function than just the snippet above can show. What if we want to type a function expression, which is very common lately due to the popularity of  **arrow functions**. So, how to do that?

```typescript
const myFunctionExpression: (arg: string, arg2: number) => void =
(arg, arg2) => {
	// code
}
```

Above you can have a glimpse of what  **function type**  looks like. It has similar look to standard arrow functions, doesn't it?

```typescript
(arg: string, arg2: number) => void
```

We supplied our variable with a function expression, in which our arguments aren't typed. That's because we've already done that with the function type and we don't have to repeat ourselves.

Function type, just like any other type, can also be used as argument's type for another function.

```typescript
function myFunction(funcArg: () => void): void {
	// code
}
```

Here as an argument, I take a function which doesn't take any arguments and doesn't return anything. Again, remember that these can be  **easily blended**  with other TS types. 😉

But what if you want to take an additional, not-required argument? How to note that something is just  **optional**? Easy - by proceeding your argument's name with the  **question mark**  (`?`)!

```typescript
function myFunction(myArg: number, myOptionalArg?: string): void {
	// code
}
```

You can have much more than just 1 optional argument. But, for obvious reasons, they cannot be followed by required arguments of any kind. There's a longer syntax for defining optional arguments, have you already thought of that?

```typescript
function myFunction(myArg: number, myOptionalArg: string | undefined): void {
	// code
}
```

Yeah, this question mark just puts your type in union with  `undefined`. And, as the same syntax for optional things is used in some more places, it's worth knowing that it can't be used everywhere. In such places, you can use the above syntax, and it'll always work. 😁

![grayscale photography of vintage car engine](https://images.unsplash.com/photo-1458942521101-2f2fb506cee3?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ)

# Some more functionalities

By this point (if you read the article naturally), you have a good understanding of some TS type - some basic and more complex ones. But there's much, much more to TS than just that! So, let's explore some interesting stuff that can make your TS life  **easier**! 👍

## Type inference

Until now, in all previous snippets, we were strictly defining our types one by one. It almost seemed like statically-typed languages require just much more writing to be done! Don't be scared my friend - they don't! Many of these languages feature so-called  **type inference**  which allows the compiler to select the proper type for a particular variable without any special annotations. So, in places where your variables are assigned to their values just when they're declared, or when it comes to your functions' return types, you can feel free to remove your type annotation and still take advantage of all static typing goodness.

```typescript
const myStringVariable = "str"; // string
const myNumberVariable = 10; // number
const myObjectVariable = {
    str: "str",
    num: 10
}; // {str: string, num: number}
```

As you can see, type inference makes our code look much cleaner and just better overall.

Type inference works by inferring  **the best common type**. This means that the inferred type is just meant to be as general as possible. So, if you want to allow only e.g. strictly defined literal types, you still have to strictly annotate them.

## Type guards

Remember unions? When I introduced them a while ago, did you think about how unions handle certain stuff? Because, you know, when a variable has type  `string`, the IDE can use that information and provide you with a number of helpful things, e.g. proper  **autocompletion**  for JS  `string`  type methods. But, when the variable has type  `string | number`  then the IDE can only show you the methods that are shared between these types. What's worse, you can only assign such variable to places where it is directly specified that  `string | number`  is allowed. But what if you want to assign something like this to type  `string`  or  `number`  separately?

Well, think about it. First, you have to make sure that your variable that can have two types is exactly of the required type. How can you achieve that? With so-called  **type guards**. And type guards is nothing more than just a fancy TS name for probably well-known to you JS operators:  `typeof`  and  `instanceof`. In TS they have no additional functionalities over JS, so you use them just like in JS. What they do under-the-hood tho, is making your variable's type  **limited to a certain type**  - in our example it's  `number`.

```typescript
const myUnionVariable: string | number = 10;

function myFunction(arg: number) {
	// code
}

myFunction(myUnionVariable); // error
if( typeof myUnionVariable === "string" ){
	myFunction(myUnionVariable);
}
```

You can also easily define  **your own type guards**  by either checking if the value has a certain property, is equal to something and etc. Such type guards take a form of functions with the certain return type.

```typescript
function isOne(num: number): num is 1 {
	return num === 1;
}

const myNumberVariable: number = 1; // number
isOne(myNumberVariable) // 1
```

Your type guard is a function that returns boolean. If it's true, your argument takes the previously annotated type. This annotation is done in the function's return type with the  `is`  keyword, having an argument's name on the left and type to convert to if the function returns  `true`  on the right. Fairly simple and straight-forward, but extremely useful when it comes to complex structures and types.

## Type casting

Typecasting (also called type assertion) is an incredibly powerful technique used in many (if not all) statically-typed languages. It's useful when you know more about variable's type than the compiler does. That's an especially common case when your compilation target is a dynamically-typed language, like JS. Basically, it allows you to  **change the type of your variable**  without any restrictions, by  **brute-force**. 👊 In TypeScript, there are 2 different syntaxes serving this purpose.

```typescript
const myAnyVariable: any = "str";

const myStringVariable: string = <string>myAnyVariable;
const myStringVariable2: string = myAnyVariable as string;
```

You can either precede the variable you cast by angle brackets (`<>`) with target type inside or by using the  `as`  keyword followed by target type. There's no difference between these methods, so feel free to choose your best.

Now, in the example above I cast the variable of  `any`  type to  `string`  but this is something that you might not even stumble upon if you don't use  `any`  in your code (**strongly recommended**). But, believe it or not, type casting has much more use-cases. Just be sure to  **not overuse it**, as it can drastically limit the type-safety of your code without notice.

# A lot to cover

If you've read this article up to this point, then congratulation - you've made  **your first steps in TypeScript**  and the world of statically-typed languages in general. But there's still a lot more to cover! So, if you like this post, consider [**sharing**](https://twitter.com/share?text=TypeScript%20introduction&url=https://areknawo.com/typescript-introduction/) [**it**](https://www.facebook.com/sharer/sharer.php?u=https://areknawo.com/typescript-introduction/) with others who would very much want to learn TS and leave a thumb up or any reaction below, to let me know  **if you want a follow-up**  where I'd discuss more advanced topics like  **interfaces**,  **generics**, and some other cool  **techniques**! Also,  **[follow me on Twitter](https://twitter.com/areknawo)**  and  **[on my Facebook page](https://www.facebook.com/Areknawos-Blog-770620059945105)**  to stay up-to-date with this series and a lot more interesting content about JS!

I hoped this article brought you some insights about TS and encourage you to broaden your knowledge. Again, sorry if I didn't explain everything super in-detail and technically, as this was meant to be a friendly, introductory tutorial. 😁 Anyway, thanks for reading and see you next time! 🚀