What is TypeScript

 

 What is Typescript?


Introduction about TypeScripts

How's it going, everybody? Cody, I'm here on staff with Learning Views. For those of you who are part of the program or people who are just coming to join us to talk, we're an accelerated web development program. If you'd like to try and participate in the community as much as possible, we're honored to have JavaScript Ola here and James to tell us a little bit more about TypeScript and doing it. We're about to program them. Oh, it still will talk too, but about four there. James, take it away.

All right, We talked a bit about what that means. Before we dive in, I just kind of want to poll the crowd a little bit and just ask: Who here has used TypeScript before? One person. Okay, who here has never even heard of TypeScript before? A couple more. Okay, perfect. Great.

So one thing I really want to point out about TypeScript is that it's a really beloved technology. It's up-and-coming. Here is the Stack Overflow 2017 Developer Survey, and you can see that TypeScript came in as the number three most loved developer language. I think that's pretty cool; it kind of speaks volumes about what people are interested in doing and what tools they are interested in using. You don't see many jobs requesting TypeScript developers a lot of the time—the jobs are kind of behind the trend. But I think that's going to catch up. I think TypeScript is definitely something you should keep your eyes on and it's worth learning, especially if you're into JavaScript.

What is TypeScript?

So, what is TypeScript? TypeScript is a typed superset of JavaScript. This means that all valid JavaScript is valid TypeScript, but not the inverse. TypeScript adds extra syntax and semantics to give you optional static typing while you're developing, and then it compiles it down into plain JavaScript. A kind of visual representation of this is right here: Everything that's JavaScript is TypeScript. That means that anywhere you can use JavaScript—like Electron, Node, or maybe jQuery in your browser—you can use TypeScript as well. In my personal opinion, anything JavaScript can do, TypeScript can do a little bit better.

So why would you want to use TypeScript? The main reason, as I said, is that it adds optional static typing. I really want to emphasize the "optional" here because you can use a compiler file called `tsconfig.json` to specify how strict you want the compiler to be. If you want really rigid code, you can make it that way, or if you want it to be a little bit looser because you're more familiar with vanilla JavaScript principles, that's fine too.

There are also object-oriented principles involved in TypeScript. I come from a Java and C# background, so this comes really naturally to me and I think it's pretty great. A lot of people don't necessarily agree, and that's fine—you don't have to use it this way. You can still use it procedurally, just like regular JavaScript. Developer features like really great IntelliSense and compiler warnings make the development process extremely easy. It's very pleasant to develop in TypeScript. One thing I do want to note, though, is even when you have compiler warnings in your TypeScript code, you can still compile it down into your vanilla JavaScript. That's just because no matter what, it should be able to provide a file that it can run. But if the compiler is telling you that there are problems, you should probably be listening. Don't trust yourself; the compiler is smarter than you for sure. In such cases, you might want to say, "Well, I know that I'm really trying to do this, but if your compiler is yelling at you, you might want to abstract or refactor your code in some way."

How Does TypeScript Works?

So how does it work? The general principle is that you write in TypeScript, you use the TSC compiler tool to compile it down to vanilla JavaScript, and then you run it in whatever environment you're using. You could be using Electron or Node, like I mentioned, and that means that if you're using an `app.ts` file for TypeScript...

Once it's compiled, it becomes an `app.js` file and you can run that and set that as your entry point into your Node web app. So here's an example. These are a few of the commands you might want to use to get started. First, you have to install TypeScript, obviously. So that's just an `npm install`. Then you'll create a project directory, enter it, and do this command `tsc --init`. This creates your `tsconfig.json`, so this is where all of your compiler rules are going to be stored. Every time you compile your TypeScript, it's going to check this file, check which rules you have turned on and off, and adjust accordingly. It's actually also dynamic, so while you're typing, it's checking this file and if you violate any of these rules, you'll get a red squiggly underline to let you know that, hey, this is not going to behave well with our compiler.


Examples of TypeScripts

Here's an example of what some TypeScript code looks like. You can see that it's almost the exact same as regular JavaScript in this example, except for one thing. Right next to the parameter `val` in our function, we have a type annotation. This type annotation is saying this can be anything; this can be any type. This is good; it kind of works a bit like vanilla JavaScript variables that can just be anything. Normally, when we have `any`, we need to do a little bit of type checking. Right now, all we're doing is `console.log` the given parameter, so we can really pass anything into it. This is just to show you what it compiles down into. You can see here that once we've run `tsc` on our file, `app.ts` or whatever it may be, the type annotation is removed because it’s not valid JavaScript syntax. If we had `: any` here in a vanilla JavaScript file, that would just hit an error and the function wouldn’t run. So the type annotations and all of the TypeScript features are to benefit you, the developer, and they really enhance the development experience. Also, this is good to note because all the extra code you might be writing isn’t going to affect your actual code base. Your deployment to your web server is going to be the size of your JavaScript, not your TypeScript.

Let's talk about the default types involved in the language. These are the main ones: `boolean`, `number`, `string`, `any`, `array`, and `tuple`. `boolean`, `number`, and `string` behave exactly how you would expect them to. A `number` doesn’t have to be any specific type; it can be decimal, binary, octal, or hexadecimal. The `any` type, like I mentioned before, behaves exactly like variables in regular JavaScript. `array` is also the same as regular JavaScript, and `tuple` is a bit of an interesting case because it's just like arrays except they are strictly ordered sets. So if you define a tuple as being an array that has one number and then one string, when you create an instance of that tuple, it has to follow that structure. If you reverse the types or if you have different types in your array, that's going to be an error. Again, that error can be ignored; you could still compile the code and it would work fine, but then you're going to hit issues that you might not foresee. So again, always just listen to the compiler.

We also have an `enum` type which, if you've used Java or C# or anything similar, this is basically just a mapping of values to developer-friendly properties. If you've got a bunch of hex codes for different colors, you could use `color.red` and it would map to something, like `#ff0000`. Then there’s also `void`, `null`, and `undefined` which can behave exactly like `void`, `null`, and `undefined` in vanilla JavaScript. Here’s just a look at what some of these might look like.

So we have a `boolean`, `number`, and `string`. Again, the `number` can be a decimal and integer; that doesn’t matter. It supports hex, binary, and octal as well as decimal. `array` is the same, and here is how you would define a `tuple`. So remember what I said before: We’re defining this as being a number in the first space in our array and a string in the second space in our array. So if we try to add a third element, that’s not going to work. If we try to add anything that’s not a number and then a string, it’s not going to work.

So then there’s also, you know, I mean the reason I actually separated `enum` from the rest is because all of these you instantiate, but `enum` is kind of like an interface. You just define it and then you can use it later. So you can say `color.red` in your code somewhere else, but you don’t say `let variableName = color.red` or something like that.

Coming back a bit to the `any` type, it can be a little bit dangerous because it’s just like any other variable in JavaScript. And what I mean by that is let's take a look at this example. Here we can pass anything we want into this function and it’ll operate exactly how we expect it to. All we're doing is `console.log` the value, and in JavaScript, you can pass anything to `console.log`. So this isn’t going to be a problem for any code. Right here are a few different examples like an integer, `null`, `hello world`, and then a new `Date` object. So it can support richer, more complex objects as well. But what if we specify a type? Now we've said, "Well, this function expects a `Date` type," and you can see the compiler is angry at us, but the output is the exact same. So the compiler is saying you shouldn't be doing this, but if we compile it down, it runs and behaves how we’d expect it. But this can be a bit problematic if you’re operating on anything explicit to that type. If you use the `any` type in your functions but you're using something exclusive to a specific type, like the `Date` object, you’re going to have a bad time.

Here’s a bit more dangerous example. Let's say we’re using this `val.getUTCFullYear()` in our value function because we know we're passing a `Date` type. This is fine; the compiler’s not going to yell at us. We don’t have to do any type checking because that’s what TypeScript's compiler is doing for you. But if you still use the same function calls down here, if you pass an integer, `null`, or a string, the results are very different. So in the first and third example, it’s just `undefined`, but in the second, you can see that it’s actually going to halt the JavaScript execution, and that could be pretty critical.

TypeScript lets you define these types using type annotations for parameters in your functions, and this lets you forego type checking in your code. You don’t need to say if this value is instantiated and blah, blah, blah, and that makes it a lot cleaner, a lot easier to maintain.

One thing I wanted to mention before I move on: I mentioned earlier the great IntelliSense you can get out of TypeScript. In vanilla JavaScript, at least in Visual Studio Code, when you’re using a `Date` object, you get all of this anyway. So you don’t need to be using TypeScript necessarily. But in Visual Studio Code, all of your user-defined types, interfaces, classes, whatever, all get the same level of methods and properties, and all of your comments are shown as documentation toward them. So the IntelliSense is really good; it’s intelligent IntelliSense, and I can't recommend using VS Code enough if you’re interested in using TypeScript.

Interfaces in TypeScripts

I've mentioned interfaces a little bit before, and they’re the first main construct in TypeScript. They’re the easiest and the simplest one, so let’s talk about what that means. Interfaces represent a contract that an object must fulfill. What that means is if you have an object, it needs to be of a certain shape. It has to have certain properties to be considered that type. So here’s an example: We have a `Person` interface with a `name` string. Now we’ve got this function that expects a `Person` object. Below, we’ve defined an object that says `name` equals James, but then there’s this extra property. So your interfaces don’t have to be explicitly fulfilled; they can be minimally fulfilled. This means that because we’ve minimally fulfilled that contract with the `name` property on this object, it’s inferred that it’s okay. It’s the `Person` type and we can use it as such. So down here, when we say `showName` and just pass this object, it’s not going to yell at us because it minimally fulfills that contract. So it lets you have any extra properties as long as you have the requirements defined in the interface.

Interfaces can extend one or more other interfaces, of course, to make complex objects. So another example would be: We have an `Animal` and it has a `name`, and then we have a `Dog`. So a `Dog` is a type of `Animal`. Again, this comes back to object-oriented programming. `Dogs` have a `breed`, but anything that is a `Dog` will have a `breed` and a `name` because it’ll be inherited. So down here we’re doing something like what we did before.

Here's a revised and streamlined version of your text with extra spaces removed from paragraphs:

Instead of defining names and having extra stuff, we're doing something a little bit different. You can see that `let myDog = <Dog>{}` is called a type assertion. It's similar to casting in other languages. We're saying this object is going to be a `Dog`, and we're telling the compiler this can only be a `Dog` object. This means it has to have exactly the properties defined in the `Dog` interface. While the contract can be minimally fulfilled by an anonymous object, it now needs to be explicitly fulfilled by a non-anonymous object because we're using type assertion to say this is a `Dog` object. We need to have both `breed` and `name`, nothing more or less.

An interesting part of interfaces in TypeScript is how they compile down into vanilla JavaScript. As mentioned before, type annotations in functions disappear when you compile because they're a developer feature. Similarly, the interface and everything set up in TypeScript disappears in the compiled code. This is beneficial because you can use interfaces and any inheritance they provide to create rich objects and data models without bloating your source code.

I've never used TypeScript with React, but I imagine if the syntax is a problem in React, you could use the `as` keyword. I don't think it's deprecated, but if you're using React data models, I'm sure there's a way to use React and this syntax together. I'll have to look into that for you because I'm sure there's a method to use React with TypeScript syntax, possibly with changes when using React libraries or packages.


Classes in TypeScripts

The other major construct in TypeScript is classes. Classes and interfaces are extremely similar; you can use one where you want to use the other. JavaScript allows you to use anonymous objects and variables, so you don’t need to instantiate new variables often, but you can and should whenever you need to maintain state in your application. Classes and interfaces differ mainly in that interfaces provide definitions. For example, if you create an interface for a service provider, it has certain properties and functions, but it won’t include the actual code inside those functions. An interface only tells you that a function should exist, but not how it works

In contrast, classes provide concrete implementations of interfaces. If you have a function like `print`, you need to provide the code as an implementation and say how `print` works, not just that it exists. So, classes are used to implement the actual details of an interface. You can use either an interface or a class based on the context, but generally, I recommend using interfaces unless you need the features of classes.

Interfaces can extend one or more interfaces, meaning all properties in one are inherited into the other. Similarly, classes can extend interfaces, inheriting all properties and methods. Classes can also implement interfaces, providing concrete implementations of the interface’s properties and methods. Additionally, classes in TypeScript can implement other classes, which requires explicitly defining all properties and methods from one class into another. This might feel a bit wacky, but it’s similar to how it works in languages like C#.

To get started, create a project directory, go into it, and start with `npm init` to create your `package.json`. Then run `tsc --init` to create the `tsconfig.json`, which defines compiler rules. Install Express and any other packages needed, including type definitions from the `@types` organization on npm, which provides type definitions for popular packages. Note that there may be versioning issues with `@types` and packages like Express, so you might need to install them directly for your vanilla JavaScript to recognize them while using type definitions for TypeScript

In this example, I wrote the `app.js` file using TypeScript, but there’s no particular benefit to writing this file in TypeScript. The benefits come when building data models and backend software. In `app.js`, I've used `any` type in the type annotations, but let’s talk about linting. Linting helps find undesirable code that isn't necessarily problematic but could be less maintainable or problematic in the future. For instance, if there's no type definition, it can help maintain standards throughout your application.

In my `tsconfig.json`, I can set `noImplicitAny` to enforce explicit type annotations. By default, everything needs a type annotation, and if you remove this rule, it won’t enforce explicit types, but the default behavior is to require them.

So here you can see, instead of TS, the left-hand side of that error is saying "T is lint," which is an extension I’ve got installed in VS Code. This indicates that we shouldn’t use the function keyword, as non-function declarations are forbidden. This helps maintain better code standards, kind of like linting—finding small issues that aren't critical but can make code messy. To fix this, we could turn this into an arrow function, ensuring our code follows a certain standard and remains cleaner and more maintainable.

Let’s take a look at what we have here. I’ve shown you the `tsconfig.json`, which contains all your compiler rules. When you run `tsc --init`, this is what’s provided for you. All the commented-out ones are default settings, but this file shows all the different options available so you don’t have to search through documentation. I think that’s a neat feature.

This is your `package.json`, which is quite basic. It includes your compiler options, and it’s a standard part of a Node app. Your `launch.json` is a configuration file created for you when you debug your application, and I'll talk about debugging and mapping files shortly. Then there's your `tslint.json`. By default, the rules block is empty, but you can add your own rules here. For example, if I show you some data models I’ve created for animals, this whole block might turn red because `tslint` prefers one class per file. If you don’t like that rule, you can ignore it right here.


Source Mapping in TypeScript

One other compiler feature I want to discuss is source mapping. This is off by default, but you should turn it on. It creates a mapping file that helps the debugger understand where your TypeScript code translates into JavaScript code. Although you can't run TypeScript code directly, the mapping file helps you debug by showing where in your TypeScript code the JavaScript came from. This is useful if a route isn’t working properly; you can set a breakpoint, and the debugger can use the map file to locate the corresponding TypeScript code, providing you with source code debugging, which is pretty great. Sure, here's your text with the extra spaces removed from the paragraphs:

So let's just take a look at our application. Something really simple. Here we go again. We can see that we're just— we've got our route here that says just go to index whenever the route is requested, and then if the form gets submitted, we can change the color of the box that our form is in. So here we've just got the default. We can do blue or green. This is kind of nothing really interesting, right? This is just showing you the way you can set up a route and pass data into your Touch application, right into the view. But let’s do something a little bit more interesting.

The big benefits of TypeScript are interfaces and classes, and these let you wrap your data into intelligent objects. So let’s say that you wanted to have a database model built into your TypeScript. You can create a bunch of models, and like I’ve done here, we’re going to be just using cats and dogs again. So we can import our cats and dogs and just uncomment this bit of code right here. Now we’re saying when we submit this form, we’re going to actually be creating a new instance of this Dog class.

Well, let’s take a look at what that actually is. So here now we’re using some of the features of TypeScript. We have an interface and then we have our classes. Notice that I’m not exporting the interface because there’s no need to make that publicly available. It can be private; it can be public; it’s going to disappear in your compiled code regardless. But our class is the one we’re interested in, so we do export that. Here we’re saying every animal has to have an ID, a name, a URL, as well as a function speak that returns a string. It doesn’t matter what’s in the function, how it operates, or what it does— let the class decide that.

The interface is just saying it needs to have one. So now we’ve got our class Dog, and it implements Animal. We have all of these different properties in our object, and when we run our constructor, they get instantiated with the given values. Now here we have our speak function that was defined up here in our interface, and down below we’re saying, okay, here is how the speak function actually operates. It’s a concrete implementation.

So we’re doing the exact same thing. Down here we have an ID and a name and a URL again. Right in here I’m assigning it a value, but if you were pulling this from a database and you created something new, you would just pass it values you’ve pulled from the database, manipulate it in some way, and then be able to save it from the database.

So let’s look at using this. Instead of just running `node app.js`, let’s say we’re interested in looking at this Dog object. In a real production environment, you would probably pull something from the database, and you’re curious to see what your data looks like. Maybe there’s a problem, and you need to investigate. Certainly! Here’s the cleaned-up version of the text:

Now we can come into the debugger. I already have this launched. A JSON file is created, but this gets created for you in VS Code, so you don't need to worry about it. The very first time you debug, it'll ask you what kind of environment or configuration you're trying to work with.

Let’s come back to our code. We can run the debugger, and there we go. When the breakpoint gets hit, we'll be able to debug.

If it didn't work, cancel out, and retry. If it says the port is in use, just close it and stop. Okay, there we go—listening on port 443.

Now, let's reopen it again. You can see we’ve just got our boxes; they’re not very interesting. You’re just submitting data through. But now we can look at our models.

We have the same form, and now we're requesting either a cat or a dog. This is how you would set up a way to bring data in from your database and present it in a view. Right now, I'm creating static objects for the sake of example. But if you had a database connection, you would pull that information, manipulate it in some way, and then present it to the user in your view.

So here, if the form submits "dog," we create a new dog and then run that constructor I showed you. It instantiates all of those values, and we can render and pass the dog's information to the user. The same goes for "cat"—we can pass that information once we’ve instantiated the object.

So it can exist throughout multiple requests and you can manipulate it in a bunch of different ways and then save it back to your database. Let’s see what that looks like here. We request a dog. Oh look, now we’ve hit our breakpoint. This is in TypeScript, so our debugger is using that mapping file even though we’re running JavaScript to say, “Hey, at this point they’ve requested to stop at,” and the TypeScript file is somewhere here in the JavaScript. Now we can say we have this dog. We can look into what it has, and in this case, it’s pretty obvious because I provided all the values right here. I just hard-coded them. But if you got it from a database, this would be a lot more useful because now you can stop and look into what your object actually has. So now we can just press play because we’ve looked at it and we’re happy with what it has. We’ve got our view. You can see that we’ve taken data from our data model, brought it to the view, and we can do the same thing with a cat. Again, it's a different data object that we’ve created, and we’ve only instantiated it in this small request scope. But again, it can live longer than that and can be manipulated across a few different web requests, and you can store it in a session, for example, things like that. So that’s the basics of doing the development, and the debugger is an important tool. If you’re not familiar with using debuggers and you kind of just always use console statements, it’s a huge quality-of-life improvement when you upgrade and start using different debuggers. I still know people who work professionally as developers and they just use console.log for everything and don’t do that.

So that’s about it, and if you want any more information,

Thank you.

Oldest