📱

Get Our Mobile App

Take your business learning on the go!

Download on the App StoreGet it on Google Play

TypeScript – Быстрый Курс за 70 минут

Владилен Минин | Result University1:08:00

Transcription

Gentlemen, hello everyone. In this video, we will talk about a programming language called TypeScript. Let's first define what TypeScript is, why it is needed, and when it should be used. TypeScript is a statically typed JavaScript that is mainly used for creating complex and large applications. It also has the codename "JavaScript that scales," as written on the official website, which I am currently on: typescriptlang.org.

Why is TypeScript needed at all, and in what cases should it be used? Here, it is worth delving into history. In recent years, JavaScript has evolved significantly. Now it is no longer just a regular scripting language, but a full-fledged programming language used for creating client-side applications, server-side applications, mobile applications, and desktop applications. That is, it is used everywhere. However, when JavaScript was created, it was not intended for large and complex applications. It originally had dynamic typing built-in, which means you could assign a variable of any type to another type. For example, if you had a string variable, you could assign an array to it.

If a language supports static typing, it means that the overall quality of the code increases, the understanding of this code increases, this code is easier to maintain, and you can catch a large number of errors at the development stage. However, without a static system, it is very difficult to build complex applications with a large number of interactions and, in general, to work in large teams, for example. Therefore, Microsoft developed the TypeScript language, which is essentially needed only at the development stage. That is, it takes the core of JavaScript and overlays new abstractions on top of it, which we will consider in this video. These abstractions relate specifically to static typing and allow us to catch a large number of problems at the development stage, write more structured and beautiful code, and so on. But we will consider all this in this video.

What are the advantages of TypeScript? Yes, if we go through the list, TypeScript can catch errors at the development stages, and when you write code, you can immediately understand that "okay, you did something wrong here," "some data is missing here," and therefore you can fix it immediately without building the application and debugging that error in the browser. TypeScript allows you to write more complex applications and do it more comfortably. Code refactoring also becomes easier because you have types, and therefore you can immediately understand where something might go wrong. It is much easier to work in development teams because each developer has their own coding style. But when there is static typing, things become simpler. And, of course, there is very rich documentation, which, by the way, can be found on the official website. There are a large number of tutorials, there is simply a description of all functionalities, paths, and so on, which is generally very cool, and you can immediately see how it works.

What are the disadvantages of TypeScript? Perhaps I would highlight one disadvantage: it is, roughly speaking, an extra layer that you need to implement at the development stage, but it often pays for itself.

Let's now move on to considering what TypeScript is. Let's look at its basic capabilities. In fact, almost everything. The only thing I would like to say is that on the official TypeScript website, there is also a playground where you can immediately see some of TypeScript's capabilities in real-time and see what it compiles into. For example, here we create a variable `message` of type `string` and assign it the value "hello." And we see on the right that it turns into regular JavaScript. That is, essentially, we just create a constant, but there are no types anymore. And everything is built on this.

Now, how will this lesson proceed? I have created a completely clean project, named it `typescript`, and essentially, here I will create files that relate to some topic, and in them, we will consider the functionality. That is, for example, we will start with types, so I am creating a file `types.ts`. It is important that it is `.ts`, not `.js`. And here we will consider some things and so on.

How do we run these files? How do we check that everything is working? Well, first of all, we need to globally install TypeScript on our computer. We can type such a command into Google, for example, and go to the npmjs website. You see that this package is downloaded more than 6 million times every week, which is generally very cool because this language is gaining popularity and will be used more and more often in various applications. For us, in this case, it is enough to write the command `npm install -g typescript`. That is, we need it globally. For this, I can open any terminal. Considering that I am on macOS, I write `sudo` here. If you are on Windows, you don't need to do this and just install TypeScript globally. And now, consequently, we have this language supported on our computer.

And how will we do it? Imagine we write some simple functionality, for example, `const str` and immediately specify the type `string`, and let's say I write "hello." How do we compile this file to see what we got in the end? For example, here I can output `str` to the console. I open the terminal and then I need to refer to this file. I think this is the TypeScript compiler, and then I specify its name. I write `tsc` and press Tab, and the file name is automatically highlighted. I press Enter, and as a result, I get a compiled file. That is, it looks like this: `1.types.js`. And here you can see that we indeed get regular JavaScript, which we can then run. For example, I can run it using Node.js: `node 1.types.js`, and we get `console.log("hello")` here. For this to work, you need to go to the Node.js website and install the latest version, for example, the LTS version, and then you can run it from the console just like I do.

However, we won't need to compile the file every time because, in principle, in WebStorm, it will already show where and what is going wrong. In general, this is the beauty of TypeScript.

Now let's start considering the basic types that exist in TypeScript and that we can create. For example, I can create a variable, say `isFetching`, and this variable will be of type `boolean`. That is, this is the first primitive type we will consider: `boolean`, and it accepts two values: either `true` or, accordingly, `false`. For example, I will have another variable `isLoading`, also of type `boolean`, and it can accept the value `false`. You can notice that we specify the types before assignment and through a colon, i.e., after the colon comes the type specification.

Let's consider the next primitive type, for example, this will be a number, and let's say I create a variable `inp` of type `number`. The type is specified using the keyword `number`, and let's say I assign the value 42 here. What's interesting is that now, even if I make the variable `inp` using the `let` keyword, if I try to reassign it, say, to a string, we immediately get an error. Yes, if I hover over it, you can see that "Type 'string' is not assignable to type 'number'." And indeed, this is a number, and even though it's `let`, we cannot assign a different value in this script because it's a different type. The same applies to boolean types and, accordingly, any others. But we will consider how to work with this situation a little later. Here I will change it to `const`.

Besides the fact that we can assign integer values, we can assign, for example, float values of type `number`, for example, 4.2. We can assign some other values, for example, and create a variable `num` of type `number`, for example, it will have the value 3e10, that is, we can also create such numbers, and as you can see, all of this is of type `number`.

The next primitive type is `string`. We have considered it in principle. For example, I will create a variable `message` of type `string`, and here it will say "hello TypeScript."

Next, we have, for example, arrays. Arrays are some data structure that contains various other data. And how do we specify in TypeScript that, for example, an array consists of numbers, or, for example, of strings? Well, first of all, we need to create some variable, say `numberArray`. Here I will put, for example, Fibonacci numbers: 1, 1, 2, 3, 5, 8, 13, and so on. And to specify that this array is an array of numbers, here I also specify `: number`, but after that, I indicate that it is an array, so I put square brackets here. However, arrays also have another form of type specification. Let's say I create `numberArray2` here, I will put the same value, and I can specify it as follows: there is a global `Array`, and then in angle brackets, I specify what this array consists of, i.e., in my case, it's `number`. In general, this notation is called generic types, and essentially, it indicates in angle brackets what this class or object consists of. If we give an example with strings, then, for example, it might look like this: `words` of type `string[]`, and here there will be, for example, "hello TypeScript." That is, we just specify the type here, and then we indicate that this is an array.

However, TypeScript has one interesting data type called a tuple. Honestly, I might mispronounce it. Its idea is that we also create some array consisting of different data types. For example, I create a variable `contact`, and it will have the following value. For example, it will have `name` of type `string`, and let's say its phone number, for example, 1234567. And we want to explicitly specify the type of this variable. For this, we can do the following: I specify the type with a colon, here I write that it is an array, and then I specify the data types: `string` and `number`. And as you can see, it works correctly.

During development, there are often situations when we still need to reassign a data type, especially considering that we are ultimately working in JavaScript, because, as I repeat, TypeScript is only at the development stage. Browsers understood only JavaScript, and they still only accept JavaScript. For this, there is a special type `any`. For example, I can create a variable, say `variable`, and assign it the value 42. But, for example, then something happens, and I want to reassign this variable. I can write `variable = "new string"`. By default, you can see that TypeScript does not allow me to do this because it remembers that a number was assigned here, and it made this variable of type `number`. But for all this to work, we can specify the special type `any`, and now we can assign anything to this variable, for example, `variable = [1, 2, 3]`, and everything is fine.

Let's consider the next basic type that exists in TypeScript using a function as an example. Here, we will also consider functions separately, but for now, I will create a function, say `sayName`, and we will pass the parameter `name` to this function. Immediately, we can specify the type of this parameter as `string`, and this function will output the value of `name` to the console, for example, like this. If we call this function, it will return nothing to us, and we can explicitly specify the return type of the function also through a colon. Here, where we finish passing parameters, we specify a colon, and considering that we are returning nothing, we can write the type `void`. This means that the function will return nothing to us. And now we can call this function, for example, with the value "Heisenberg." Let's try to compile all this and make sure there are no errors. `tsc types.ts`. I will clear the console and `node 1.types.js`, and we get the line "Heisenberg." Okay, let's move on.

There is another interesting type in TypeScript that was announced in relatively new versions, which is called `never`. In what cases should we specify this type? In two cases: when a function either returns an error and never finishes its execution, or when it is constantly doing something. For example, I can create a function `throwError`. Here we will receive some `message` of type `string`. For example, something happens here, say some `for` loop: `let i = 0; i < 10; i++`, and for example, if `i === 5`, then we will throw an error `throw new Error("message")`. So, if we have such functions, we can specify the `never` type. If I hover over this type, TypeScript tells me that a function that returns `never` cannot reach the final point. If I temporarily comment it out, then TypeScript thinks that it might not reach this error, apparently I wrote this loop in vain. Let's remove it and leave only this example. So, if the compiler clearly sees that an error will definitely be thrown here, then with the `never` type, everything is fine. There is also another case: for example, we will have a function `infiniteLoop` that will do a `while` loop with the condition `true`, that is, this loop will run constantly. This function will never finish, so we can also specify the `never` type here. I just won't run this function because otherwise everything will freeze.

Okay, let's move on. And next, I want to consider a special construct that exists in TypeScript, which essentially allows us to create our own types. For example, we can use primitive types and create aliases for them. For this, there is a special keyword called `type`. For example, I can create a type, say `Login`, and it will have the value `string`. Now I can use this type for some variables. For example, I can create a variable `login` (lowercase) and it will be of type `Login`, and here I will assign the value, for example, "admin." But I cannot create a variable, say `login2`, of type `Login` with the value of the number 2, because in reality, `Login` is just a string, and in this case, we are creating an alias, but sometimes it is convenient for development because named types give more understanding of what is happening.

We can also specify potentially different data types for the same variable using `type`. That is, for example, situations arise when an ID can be either a string or a number. Therefore, I can create a type `Id` that will accept a value of either `string` or `number`. And thus, I can create `id1` with the value 1234, and everything is fine. Even if I type `id1` as `Id`, I can also create a variable `id2` of type `Id` with a string value "1234", and everything is fine. But, for example, I cannot create `id3` of type `Id` and assign it a boolean value, because it can be either a string or a number. By the way, such things, when we compile them into JavaScript, they simply disappear. That is, they are also needed only at the development stage. Yes, if we look at the compiled file and go to JavaScript, we won't see these types anywhere. That is, they simply disappear. What's interesting is that the comment I wrote with the type specification is left here, but it's clear that they are simply not there.

Okay, let's move on. And the last two primitive types that I would like to consider are `null` and `undefined`. You know that these data types also exist in JavaScript, and they have been added to TypeScript as well. They are not used very often and are mainly used for defining types. For example, I can create a type `SomeType`, and for example, it will accept a value of either `string`, or `null`, or, for example, `undefined`. That is, you can see that these types are highlighted, they are naturally present. That is, you can often encounter them precisely in this context. But if we specify an empty type for a function, meaning the function returns nothing, then `void` is usually used.

Okay, let's move on. And next, I will create a new file. Let's name it with an underscore because there might be some problems with spaces. Next, we will consider interfaces. `interfaces.ts`. Here, let's also delete the JavaScript and rename it with an underscore.

What are interfaces and why are they needed at all? Essentially, we create a type that is mainly used for objects or classes, where we specify what fields, what functions, and what general elements these objects should have. And again, interfaces do not compile into anything. That is, they are also needed only at the development stage. Let's consider a simple example. For example, to create an interface, I write the keyword `interface`, and for example, I created the interface `Rect`. What parameters can a rectangle have? For example, it can have an object. Let's make it simpler: `id` of type `string`. And what's cool about interfaces is that I can immediately say that this field will be read-only, meaning we cannot change it. And for this, I can use the `readonly` modifier, which is also characteristic of TypeScript. Next, for example, we will have a `color` property of type `string`. And again, what's cool is that I can add these parameters as optional by adding a question mark. And now we will see an example of how it works. And for example, I can specify some other objects here, say a `size` field, it will have parameters `width` of type `number` and, for example, `height` of type `number`. We have created an interface, and now we can create various objects that will be of type `Rect`. Let's see how it works. For example, I will create a variable `rect1` and immediately specify its type `Rect`. This will be an object, and you can see that now the `rect1` variable is invalid because we have not implemented the necessary fields. So, the first field is `id`. I want you to pay attention to the following: I wrote the letter `i`, and immediately in WebStorm, it automatically suggests which fields can and should be implemented. That is, autocompletion is simply amazing when we use TypeScript. As for `id`, it should be a string, so here I write, for example, "1234". Then I need to implement the mandatory `size` field. This is an object that has a `width` field. So you see, I just write one letter, and then press Enter, and the development process is very accelerated. For example, its width will be 20, and its height will be 30. And now our `rect1` variable is valid. But we can also add, for example, `color` to it, say "blue," and everything will work fine. Let's say we create another variable `rect2` of type `Rect`. Let its `id` be "12345". Let its `size` be different, for example, `width` 10, `height` 5. And here I can not specify `color`, but for example, I can specify `color` somewhere below, say `rect2.color = "black"`, and you can see that it works correctly. The only thing that might confuse you is that `rect2` is a constant, but later I change it because ultimately we are still working in JavaScript, and in JavaScript, things that are constants, if they are an object or an array, we can change their internal state, but we cannot reassign them, that is, write `=` and some other value.

Also, I want to discuss two more points with you that we can do with objects and interfaces. So, we can specify to which type an object will belong. For example, I create a variable `rect3`, and by default, it will be an empty object, but I can strictly cast it to some type by writing `as Rect`. And now this object will subsequently be of this type or be considered this type. There is an alternative older syntax: `rect4 = {}` and then we specify what type it should be cast to, what it should be considered. Here I will put a comment now because we are moving to the next logical block, specifically to interface inheritance. That is, TypeScript allows us to interact very flexibly with interfaces, and let's consider this using our `Rect` interface as an example. So, it has some mandatory parameters, optional parameters. I forgot to show that if we, for example, try to assign a new value to the `id` field of `rect2`, we see an error, yes, because it is a `readonly` property, and therefore it does not allow us to do this.

If we return to interface inheritance, then we can inherit. For example, I will create a new interface for this, say I will call it `ProjectedRect`, and this interface will inherit from the `Rect` interface. Here I can add some new fields. For example, this interface will require the object to implement a function that will calculate the area of this figure. For example, I will call it `getArea`, and this will be a function, so I specify `: () => number` now, and after the colon, I specify the data type that should be returned. In our case, it's `number`. Now I can create some new `Rect`, let's call it `rect5`, of type `RectAndArea`, and let's put all the necessary fields into it. For example, its `id` will be "123", let's say its `size` will be `width` 20, `height` 20. How can we add it? But we must add the `getArea` function. That is, if we look, WebStorm is already suggesting that something is wrong, so I implement the `getArea` function. We can explicitly specify here that it returns `number`, but again, you can see there is an error, because the function currently returns nothing. Therefore, here I can write `return this.size.width * this.size.height`. And now everything will work correctly.

Interfaces can also interact with classes, and we will consider classes a little later. But for now, let's consider an example where we create some interface. Let's say I call it `ILog`, and very often interfaces are named starting with a capital letter `I`, which indicates that it is an interface. And for example, we will have a `time` field of type `Date`. And a class that inherits, or rather implements this interface, must implement the `setTime` method. And we can also specify methods like this: it will accept some `date` of type `Date`. Here, let's also add `date` as `time`. Now we can create some class, say `SimpleClock`, and for TypeScript to understand that this class implements the interface and must implement its methods, we write the keyword `implements` and the interface name `ILog`. Now you can see that this class is invalid because we need to implement the `time` variable, for example, of type `Date`, and by default, it will be equal to `new Date()`. And we also need to implement the `setTime` method. That is, what has happened now? I started typing `s`, and the completion of the `setTime` function that I need to implement appeared. I pressed Enter, and considering that we have already specified all the incoming parameters and outgoing parameters, WebStorm automatically fills it all in for me, also thanks to TypeScript. And, for example, here we will write `this.time = date`.

And the last thing I would like to tell you about interfaces, I will also divide this into a magical chunk. There are situations when we need to create an interface for an object that will have a large number of dynamic keys. That is, for example, I will create a variable `css`, and it will be an object that, for example, will have a `border` property: "1px solid black", for example, it will have a `marginTop` property, for example, 20px, for example, `borderRadius`, say 5px, and so on. And of course, we can describe an interface for this object. That is, we can write `interface Styles`, specify `border` of type `string`, and so on. But we cannot, for example, list all properties, it will be simply inefficient. And for such situations, we can specify a special syntax. Let me immediately apply it to `cssStyles`. Yes, now errors are highlighted.

Because we only specified border, there are already some present, and we're applying border-radius, so the tactic reads that this is an invalid entry. But in order to make it valid, we can specify it as follows: in square brackets, we specify the type of the key, and for this, there is the keyword key, for which we specify the type, say string, and as a value, the type will be string. That is, the data, this entry relates precisely to border, magenta, border-radius, and so on, and this string relates precisely to these values. Okay, with interfaces, it's more or less clear. Let's analyze now, we have enums. What are enums? Essentially, it's a helper entity that allows you to better structure your code if there are some, say, similar elements. And it has, let's look at the entry, let's consider each of them. That is, to create an enum, there is a special keyword enum. And I got something unclear, say, and enum will be membership. Further, in curly braces, we simply specify what values this enum will have. Let's say simple, through a comma, say, standard, and for example, premium. Now, how do we use these enums, and what values will they have at all? Well, for example, I'll create a variable membership, and for example, I'll put the value membership in it, say, standard. Now, if we look in the console at what membership is, then first we need to compile everything. That is, I clear the console and write node enum.ts, and we get one here. That is, by default, if we define enums this way, they are assigned values 0, 1, 2, and so on. However, we have the opportunity to get, say, a string value, say, premium, or any other element. This is called reverse enum, when we can, for example, create a variable membershipReverse, here we say that we are interested in enum membership by key 2, and now, if we output membershipReverse to the console, then again we will need to compile this file and run it, and now we get the string premium here. Yes, but this is, roughly speaking, we referred to the index and got this string. We get it this way because it's a very unusual implementation of enums in JavaScript. I won't show it now, there's no particular point in it, but if you want, read about it. So, they are usually done this way. I wanted to tell you a little more about enums, that we can assign them some more meaningful values. That is, for example, I can create an enum, say, social media, it will be an enum. Here we write it without an equals sign, and for example, what social networks do we have? Say, VK, which will be equal to the string, for example, VKontakte. Let's write VK. Then, for example, Facebook equals Facebook, and for example, Instagram Instagram. And now, if we access any of the elements of this enum, we will get a string. That is, for example, I'll create a variable show, say, it will be social media, say, Instagram, and look at it in the console, show. Then compile enum.ts, and run it here using node enum.ts. We get Instagram here, respectively. Yes, that is, if we explicitly specify a string, then instead of basic numbers, we will get a meaningful value here, which can be very convenient. Okay, we move on. Next, we will consider functions. Functions.ts. In principle, we have already considered how functions work, but let's break it down in more detail. Say, I'll have a function, say, it will be called add, it will add two numbers, respectively. We accept two numbers here, say, a of type number, and for example, b of type number, and this function should also return a number. Here I write number, and in principle, I'll write simple logic: a + b. So, the idea here is that we also specify the incoming parameters through a colon, their type, and through a colon, after we finish passing parameters, we specify the type that this function should return. If we consider an example with a string, it will look something like this: for example, a function toUpperCase, it will accept a string of type string and should return a string. Well, for example, here we can do the following: str. Then TypeScript understands that this is a string because we specified the type, and we will immediately see what methods a string has, for example, the trim method, and for example, the toUpperCase method. Because when we write trim, TypeScript knows that trim also returns a string. Yes, and we can again call some methods on the string. However, functions in TypeScript have another interesting functionality when we can overload certain parameters and overload a function, that is, call a function with different parameters and get different values. Let's consider, say, some abstract example. Say, I'll create a function position, and if this function receives nothing, then it will return the interface position. Let's say it occupies a position. Now, this interface needs to be created. Interface position. Well, for example, it will have the following return type, say, x will be either a number or, for example, undefined. For example, y is the same, number, and for example, undefined. For now, this entry is not visible, but here we are specifying an overload of this function. Now, for example, I'll write the following functionality for this function: function position, if it accepts one parameter of type number, then it will return, for example, the type MyPosition, it will be default, which I also need to create. Here I choose these curly braces. For example, I'll invent some interface MySupportsDefault, an interface that will inherit from MyPosition, and for example, it will contain a field default of type string, and for example, another overload. For example, the function position, if it accepts 2 parameters, a of type number and b of type number, then it will simply return MyPosition. Now we have defined the potential ways to call the position function, and now, how do we define the position function itself? I write using position, and we know that this function can potentially accept a maximum of two parameters, a and b, but they are not mandatory, because we can also call position without parameters. What I write here is a? of type number, because this parameter may not be optional, and b? of type number, and the implementation of the function will be as follows: that is, first we need to check if we did not pass parameters a and b, that is, if not a and if not b, then we need to return MyPosition. We support this object which has parameters x and y which can be either number or undefined. Here I can write return x: undefined, for example, y: undefined. If, for example, the second case is when we passed only parameter a, then we check if a and if not b, then we need to return the MyPosition default interface, that is, then we can write return { x: a, because we passed it, y: undefined }. And by the way, in this case, undefined is also not a variable type, because we are inside an object, and we also need to return here a variable default: "default". For example, toString. Yes, because it's a string. Otherwise, if these did not work, then we also need to return MyPosition. Therefore, if you write return { x: a, y: b }, yes, and now it is visible that we have no errors, and we can work with this function. Let's try to call it, for example, in the console.log, I will write the following: position, for example, here I will write a comment: empty. Then I will have console.log one number, for example, I will write position with the value 42, and here I will write super empty. I will call position with the value, say, 10, 15. Let's compile this file as well: tsc functions. I'll clear the console and write here node functions.js, and we get, in principle, the expected result. Yes, that is, now we have specified overloads for the function and can call it in different ways. Very often, this can be found in various libraries, for example, in Lodash, in RxJS, and so on. There are many such entries, including in Angular, which uses TypeScript. Well, we move on, and the next module that I want to show you is called classes.Classes.ts. Classes in TypeScript are created the same way as in JavaScript classes, but there are certain nuances that we will now break down. That is, the most basic example here can be creating a class, for example, TypeScript. Here we can specify various fields that will be in this class, for example, version, of type string. Then we can specify a constructor, for example, which will accept a version of type string, and we can immediately write it to a private variable version. Well, for example, we might have some method, say, inform, which will accept a parameter, for example, name of type string, and it will simply mean a string, for example, if I specify in square brackets name, TypeScript version is, and here I specify this version. Yes, that is, everything here is the same as in regular JavaScript classes, but at the same time, we specify certain types. Let's consider another example. What are the differences? That is, for example, I'll create a class App, and then I can specify fields that will be present in this class, but for example, with the readonly modifier, which allows us to determine at the compilation stage that these variables will not be overwritten in any way. Let's have a field model of type string. Let's have a readonly field numberOfWheels, the number of wheels, of type number, and by default it will be equal to 4. Then, for example, we will have a constructor, and it is considered good practice, by the way, if we define fields before the constructor, then let the constructors, and then the specific methods follow. Reading code this way is much easier. For example, here we will pass a parameter d model of type string and assign it to a private variable this.model = d.model. What is interesting is that given that the model field is readonly, we can still overwrite it, but we can only do it inside the constructor. In other methods, we cannot do it. And by the way, there is a shorter version of such an entry, that is, when we accept a value in the constructor and want to write it to a field, we can write it more concisely. Let me show you how. That is, I'll create the exact same functionality but more concisely. Say, we will also have a readonly field numberOfWheels, and given that we pass the model to the constructor, we can write this syntax: constructor, here specify the modifier, for example, readonly model of type string, and that's it. Then TypeScript will do everything else for us. That is, this entry is completely identical to what is written here. Given that we specify the modifier directly inside the constructor, then TypeScript will create a readonly field model in the class, and in the constructor, it will simply assign it the incoming parameter model. Now let's break down what else has appeared in TypeScript that relates to classes, specifically it will be about modifiers. For this, let me create another example. For example, I will have a base class Animal, and what are modifiers? That is, there are three types of modifiers: protected, public, and private. Let's immediately figure out how they work. That is, for example, I can create, say, protected elements, say, wheels of type string, and by default, it will be an empty string. For example, I can create a public variable, say, color of type string, and by default it is black. By the way, if we don't write any modifier, then by default they will all be public. That is, public we simply explicitly specify. There is also the private modifier, and for example, we can also assign it to functions, say, private call, here it will be, for example, console.log. We have the Animal class, and we want to create a more specific class that will inherit from Animal, say, class Cat, which inherits from Animal. What can be in the Cat class? Well, for example, we will have a method, let it be a clearly public method setWheels, which will accept wheels of type string, it will not return anything, and all it will do is access this.wheels and assign it the value wheels. Now I will explain how modifiers work, and then we will create a variable cat = new Cat, and now let's see how it works. First of all, when we set the protected modifier for certain properties, it means that these fields are not accessible, first, in the Animal class, and for all classes that inherit from the Animal class. That is, it is visible that given that these are protected properties, we still have access to them in the Cat class and can safely assign something to them. For example, we have a private method call, and yes, of course, I forgot to explain that when we create a variable like this, an instance of the class, we cannot access wheels because it is protected. Yes, so we cannot do this. We can only work through the setWheels function. Now, what about private variables or methods? They are accessible only in the class in which they were defined, that is, in Animal, we can safely use the call method, and everything will be fine. Yes, for example, in the constructor, we can safely call the method this.call, and everything is normal. However, in the Cat class, we can no longer access the call method because it is only available in the Animal class. Status, if we write this.call here, there will be an error because it is a private property. But at the same time, there are public variables or methods that are accessible to all instances, that is, they are accessible to the Cat class, to the Cat class, and so on. That is, you can safely write, for example, cat.setColor("test"), the cat will say "test", or for example, we can access the public property cat.color, and everything is fine. console.log(cat.color). Okay, that's about modifiers. Also, in TypeScript, there is another interesting concept, which is abstract classes. What are they for? That is, they are not compiled into anything, but they are also needed at the development stage for us to inherit from them. And besides that, there are also abstract methods. That is, it is written approximately as follows: we create an abstract class, for example, Component, and what can be in this class? Here we simply describe some methods that must be implemented by classes that will inherit from this component, and they will also be abstract. For example, a render method that returns nothing, and for example, an abstract method, say, info, which will return a string. Now, if we create some class, for example, AppComponent, which will inherit from Component, then here we need to implement the render method, and it is visible that again, autocompletion suggests what to do with it. It returns nothing, so here I can write console.log, for example, "Component is rendering". And I also need to implement the info method, which returns a string. That is, this is. And here I will change the double quotes to single quotes. Yes, that is, an abstract class, and we can inherit from them, but at the same time, they are not compiled into anything. And if we look at the result of such an entry, compile the classes file and run it.js, then we have some call, black, this probably relates to the animal, but we are more interested in JavaScript, and here we see a lot, a lot, a lot of various functions that are actually classes. That is, in this case, the TypeScript compiler is configured to compile TypeScript into ES5 syntax. But if we compile it into ES6 syntax, then in principle, we will not see any abstract classes, these things. Okay, we move on, and the next module that I want to show you is called utilities.ts. Essentially, what are utilities? These are some auxiliary constructs in TypeScript that allow us to work with types as well. And for example, let's create some function, say, I'll call it process, and this function can accept a parameter, for example, x, which can be of type either string or, for example, number. And depending on the type of this variable, we need to do different functionality. For this, we can use the typeof operator, which is in principle present in regular JavaScript, and ask what if typeof x is, say, equal to number, then we want to return, for example, x.toFixed(2). Such a method is present specifically for numbers. However, otherwise, if it is a string, then we want to return, for example, x.trim(), because this is a string, and TypeScript already understands in this case that this will be a string. Then, then there is an interesting operator called instanceof. It is also present in regular JavaScript, and with its help, we can check the belonging of any object to a class. For example, I'll create two classes. For example, Response, it will have fields, for example, header, say, ResponseHeader, and for example, it will have a field result. And there will be another class, very similar, which I will call, for example, MyError. It will have fields, for example, header, but instead of result, it will have message. Message here is error header, and error message. There are two classes, and for example, we have a function that processes both Response and Error simultaneously. For example, I'll call it handle. Here we receive some object response, and it can be of type either the class MyResponse or the class itself Error. And in this case, it's unclear how to process this object, because it can have either a message field or a result field. The header is present in both. Therefore, we can ask, what if response instanceof MyResponse, then in this case, we can already interact with its fields, for example, create a new object. Let's just return, for example, a field info with the value response.header + response.result. Yes, and here it is immediately visible that TypeScript suggests it because it knows exactly that result in this case is of type MyResponse. And otherwise, we will return an object with the field info: response.header + response.message. That is, in this case, TypeScript also understands that the result field is no longer present, but the message field is present, thanks to this operator. And for example, we can also consider another situation, it occurs quite often. That is, for example, I will have a special type that I will call, say, AlertType. This is a type that will accept two values, for example, success, for example, warning, and for example, I can create another value, warming, like in Bootstrap. And very often in practice, this particular construction is found. And then, for example, I will have a function setAlertType, which will accept a parameter, say, type of type AlertType. Here it will do something for us, it's not particularly important now. Now, to this function, I can pass various values, for example, success. Everything is fine. I can pass the value warning. But if I pass some value, for example, default, then it is immediately visible that we have an error. Yes, because in the type that we accept, there is no such value. And therefore, at the compilation stage, we immediately see that such a value will be invalid, and consequently, we will have some errors. Next, we will talk a little about generic types. Generics.ts. I'll close all this. Wait, we have already touched upon generic types. Let's just repeat. For example, I'll create a variable array of numbers, and we can specify the type using generic syntax. That is, I refer to the Array class, which allows us to create arrays, and specify in angle brackets what this array will consist of, of numbers. Well, for example, I'll also put Fibonacci numbers here: 1, 1, 2, 3, 5, and so on. That is, I repeat that here we specify what this object is, and in angle brackets, we specify what it consists of. However, there are situations when, for example, the same function can work with different types of data. For example, a function reverse, which simply takes an array and reverses it. In this case, I'll write a simple implementation. That is, the reverse function will accept an array, and it will simply return the built-in array reverse function. But arrays can be of different types, yes, they can consist of, for example, numbers, strings, or for example, they can be tuples with types. This example consists of numbers and strings. And how do we make one function, with type specification, work with different types of data? For this, we can also use generic types. Here we can specify that this function works with type T. Then we accept an array that is an array of type T, and we will also return some array of type T. And this parameter will dynamically adjust to the values that we pass. For example, I'll create several array of strings. For example, these will be settings, and for example, here I'll write "hello", "world". Now we can call the reverse function with different types of data, and as you can see, everything will work correctly. Yes, because here we have specified some generic type that will adjust to the corresponding content that is in this array. And the last thing I want to cover with you in this lesson is some auxiliary operators that are also present in TypeScript and allow us to interact with types. Let me call it operators.ts. For example, I'll create an interface, say, Person, which will have two fields: name of type string, and field age of type number. Now, using this interface, I can create a separate type that will consist of the keys of this interface. I can do this by writing type, say, PersonKeys, and then I can apply the key operator to the Person interface. And now, as a result, in the PersonKeys type, the following values can be present: name, which is a string, or age. That is, we can easily verify this, for example, by creating a variable name, it will be of type PersonKeys, and here I will put the value name. Yes, I didn't quite understand what the error is. That is, my name. Yes, perhaps it just doesn't know that it confused the name. That is, it is visible that in my name, we can safely put a variable, but we can also change it to these calls. And to make it more correct, we can change it to age, and everything should work well. But at the same time, we cannot put, for example, the value job in it, because it can only accept these two values. In some situations, for enumerating keys, this can be useful. And another example, for example, I'll create a type User, it will be a type where we will specify some metadata plus regular data. For example, underscore ID, number. For example, we work with ORM, Mongoose, and there is a similar construction. Mongoose will have fields, for example, name of type string, for example, email of type string, and for example, it will have a field createdAt, of type Date, for example. Suppose we want to use this type and at the same time create our new type, but without including some fields, for example, the ID field, for example, createdAt. We can do this in two ways, essentially. I create a new type, say, UserWithoutMetadata, and the first way is to apply a special keyword called Exclude. And then in angle brackets, we describe what exactly we need to do. That is, first of all, we use the key operator on the User type, and then through a comma, we explain to it what we need to exclude. That is, we need to exclude the underscore ID field here, and we also need to exclude the createdAt field, for example. And now, in this type, only the fields name and email are present. There is another similar entry for creating a similar interface, a similar type, that is, UserWithoutMetadata. We can use another keyword, Pick. And then in angle brackets, we specify the type we are working with, User, and then through a comma, we specify which fields we need to take, respectively. In our case, these are the name and email fields. Yes, and now we can only here, this is one method, the second. And now we can create various variables that will accept such values. For example, const user1 = new. If I do this, and for example, try to put the value user1 = underscore ID in it, then of course, it will work like this, and I haven't specified the type UserWithoutMetadata. What we get here is an error. Yes, because here we can only assign values name and email. Exactly the same story with this type. And in principle, that's all there is in TypeScript. That is, of course, in the documentation, there are some other fields that you can read about, for example, about namespaces, about modules, but for the most part, many people think that TypeScript is a separate programming language, although in reality, if you look at it, perhaps only these things are new. Everything else is regular JavaScript. That is, we can safely write without types in TypeScript and get the same result. Therefore, I hope that now it has become clearer to you how TypeScript works in general, that there is nothing complicated in it, and that in reality, for large applications, it is a very cool and beneficial solution. If you are interested in seeing TypeScript in action, then I have a whole course on Angular 8+ where at the core of this framework is TypeScript, and in the development process, it is applied very effectively and coolly. Therefore, I will put the link in the attached comment. But if you are simply interested in this content and found it useful, then I will be glad if you subscribe to the channel, like it, leave a comment, and if you also turn on the bell if you are interested in the content of this channel, then I will be very grateful, as such things help promote videos on YouTube. And with that, I say goodbye to you and see you in other videos.