An Introduction to TypeScript

Kitson Kelly speaking at JS Monthly London in March, 2017
2389Views
 
Great talks, fired to your inbox 👌
No junk, no spam, just great talks. Unsubscribe any time.

About this talk

You likely have heard of TypeScript or are currently using it. This talk will quickly give some background on TypeScript and discussion on why TypeScript is the way it is. We will also cover off some of the more interesting and powerful features that have come to TypeScript recently and how they can improve your code, increase your productivity, and impress your friends.


Transcript


- [Kitson] I'm going to talk about TypeScript tonight. First, a little bit about me. So I'm CTO with a company called SitePen. We do sort of two major open-source projects that we focus on, Dojo and Intern, which is a testing harness. And predominantly, what we do is build web applications for customers. I've been with SitePen now as CTO for about 18 months. Prior to that, I worked for NOW TV, part of Sky, and ran the technology for them. So I'll talk a little bit first about life of the modern developer that we live in. And, I mean, I think one of the challenges that we probably all face is those d'oh moments, when, you know, if you get mad at me every time I do something stupid, then I'm just going to have to stop doing stupid things. Right? So like Homer Simpson, we find ourselves in an increasingly complex environment, things far more difficult to do, working on large teams, sort of geographically dispersed. You know, lots of different challenges. And I think the other thing is there is a bit of a development culture. Sometimes us, as developers, are kids. You know, we want it all now. We want it. Mine, mine, mine. Why doesn't this work? This should be easy. So particularly, when that comes to JavaScript, that's been a challenge since the language was invented in two weeks, as a sort of "must get it done" by Brendan all those years ago. So I think TypeScript kind of set out to try to solve some of those solutions. I've been working with TypeScript for about two years now. Before I joined SitePen, I was still a Dojo committer at the time, and the team at Dojo were saying, "Well, we've been using TypeScript on a few of our customer projects. It might be a good candidate for Dojo 2. You know, you should take a look at it." And actually, Bryan Forbes, who's a Dojo committer and also works for SitePen, said to me, "Well..." Because I was knocking it, saying, "Why should we go through and transpile our code? You know, we used to make fun of CoffeeScript all the time because you were sitting there writing one type of syntax and translating into another. Why is TypeScript any better than that?" And his simple response to me was, "Well, have you actually tried it?" And so I did about two years ago, and I was sort of impressed. And going back and understanding that a bit better, and understanding TypeScript a little bit better I think has helped me become a better developer in whatever situation that you might be using TypeScript in. So the first question of the evening is, does anybody know what these four things have in common? There's a couple. I'm actually familiar with the first two. For some reason, I missed out C# and jumped into JavaScript, and then suddenly found TypeScript and I skipped over those. There's at least one person who knows. Anybody else who knows what these four things have in common? Okay. There's a couple others. What they have in common is Anders Hejlsberg. So Anders is the lead architect on TypeScript. He was also the lead architect on Turbo Pascal. He was the lead architect on Delphi. And then Microsoft came along, threw a bunch of money at him, he left Borland and joined Microsoft and delivered us C#, or C sharp. I think what happened then is in about 2010, 2009, somewhere around there, they pulled him off of that and said, "JavaScript is a pain in the butt and you need to fix JavaScript. And you're probably our best language guy that we have. So why don't you figure out how we fix JavaScript?" And that gave rise to the birth of TypeScript. And so it was announced in October of 2012, after two years of Version 0.8 in 2012. This was after about two years Microsoft had been using it internally, trying to get it to deliver JavaScript for them. It focuses on enterprise development or "JavaScript that scales" is sort of the tagline for it. And it's a superset of JavaScript. Ideally, and there are some exceptions to it, is pretty much every JavaScript that you can write is valid TypeScript, and TypeScript lays features and functionality on top of that. And particularly, it adds strong typing to what is a weakly-typed language. And the other, it does a few other things, which I'll talk about, too. And for me in particular, that sort of weak typing, while I'd become very accustomed to it in JavaScript, I hadn't realized that that was actually one of the banes of my development existence. Right? Because especially when you pick up a new function and you're trying to discover an API, and you're trying to figure out, "Well, so it takes a parameter called 'Options.' What goes in there? Is it a string? Is it an object? What are the valid properties on that object?" You know, all those sort of things are all left up to guesswork, and you've spent more of your time actually reading somebody else's code than actually writing functional code yourself. And TypeScript's sort of dramatically increased in popularity. So this is the RedMonk January 2017 chart. TypeScript entered the top 20 in that survey, which they do every six months. It went from just outside the top 20 to 14th place. And JavaScript, for its sins, is top place, followed quickly by Java. So TypeScript has really sort of dramatically gone up there, along with things like Rust have picked up quite a lot and is obviously a big entrant in there, too. So it's kind of in that adoption level. Also, just to compare, the popularity of TypeScript has, you know, Google Trends isn't always the best thing, but this was taken earlier today. You know, TypeScript sort of outstrips Dart. For those that aren't familiar with Dart, it was Google's sort of strongly-typed alternative language to JavaScript. So it transpiles to JavaScript. It's JavaScript-like. But it obviously isn't directly JavaScript. And we've got good old CoffeeScript. It was popular in its day, but it was never hugely popular. And you can see, things like Rust, it's still sort of outstripping things like Rust. Even though Rust is becoming increasingly popular, particularly in the server side and in, you know, traditional places where you would use C++. But let's get practical for a moment and take a look at whether you can get employed with TypeScript. I think obviously, Angular 2 and Angular 4, and Angular whatever number comes up next week, have been driving a lot of that popularity in TypeScript. But I think there's a lot of other uses for it. So increasingly, we're starting to see jobs in the marketplace, as developers that are specifically looking for TypeScript capabilities. And as we know, Angular 2 and beyond use TypeScript. VS Code, which is the open-source editor from Microsoft, is written in TypeScript and runs on an embedded browser, pretty modern for Microsoft. And you have things like Dojo 2, which I happen to be the project lead for, is written in TypeScript. You have RxJS, the ReactiveX extensions to Observables, and Ember, particularly in the backend tooling, but they're continuing to look at how they can adopt that wider within Ember is also done in TypeScript. So for me, when approaching TypeScript, and this took me a long time, I started working quite closely with a lot of the core TypeScript team, sort of champion the sort of things that we needed for Dojo 2. But also, learning how we solved a lot of problems in TypeScript at a fairly fundamental level to make sure that we were delivering something that would be an effective toolkit and framework. So these are some of the things that I think were important to me to learn early on with TypeScript, and understand the way that it works and the way that it thinks. So it is JavaScript. So if you don't have a good understanding of JavaScript, you will probably struggle a bit with TypeScript. Having some good fundamentals in JavaScript are a boon to it, partly because it's so strongly-typed. If you come to it from another typed language, like, for example, Java, and you try to pick up TypeScript directly, it will frustrate the hell out of you, simply because it isn't like other strongly-typed languages. It's like JavaScript that is strongly-typed, and JavaScript is unlike any other language that you probably have dealt with, and has some fundamental differences from a lot of other languages. But if you have a strong JavaScript background and you've been doing JavaScript for a number of years, TypeScript can often be familiar. And if you think about the ways that we solve problems in JavaScript, the TypeScript solution isn't far from that. And ultimately, though, it's JavaScript with a level of intelligence. And in particular, using TypeScript and the TypeScript services, which is sort of the backend API, and having your IDE integrated to those services is really where you get a level of productivity out of that, and I'll show you that a little bit later on. But that's the biggest thing, is TypeScript is evaluating and looking at your code as it's written, and figuring out how the code is likely to be run in the Runtime environment. And showing you the challenges and the information about what's actually happening with your code as you write it, which is a lot more powerful than saving a file, refreshing your browser, and hoping for the best. I think one of the core things, especially if you've come from another strongly-typed language, is what is very logical from a JavaScript perspective is TypeScript is essentially a structural typing language or what is sometimes called "duck typing." If it looks like a duck and it sounds like a duck, then TypeScript treats it like a duck, right? So if it has the same shape of something that you're trying to make an assignment to, it doesn't actually have to be an instance of that constructor. It doesn't have to inherit from that particular class. If it looks like something that fits this particular hole, then TypeScript will allow you to do that. Why? Because essentially, that's the way JavaScript works. JavaScript doesn't really concern itself with inheritance chains. This does cause one big problem. There's one form of nominal typing in JavaScript, which is the instance of Operator. And the instance of Operator only evaluates true if something is nominally inherited from that or has that in its prototype chain, which can cause lots of problems. And it's one of the things that TypeScript has struggled with from the get-go because everything else in the language is structural. But there's this one little bit of nominal typing. There's some changes in TypeScript 2.2, which I'll talk about a little bit, which improve that. But the TypeScript team are still trying to figure out the best way of introducing some form of nominal typing without actually breaking the way that JavaScript works, which is pretty hard. And I think one of the more powerful features, this started in TypeScript 1.8, so probably about nine months or so ago, is code flow analysis, which injected a lot more intelligence into the process of analyzing your code, as it was statically done. So I'm going to show you some things in TypeScript. I haven't ever done this particular set of live coding before, and again, I don't know what's most interesting to you. So I'd be more than glad to take some questions or answer anything during this period as well, too. But we will see how this goes, so it's a bit of an experiment. And we'll make sure we get this out to the people. I have pretty much everything pushed up to GitHub at the moment that I have in this project. And anything that we do here, I'll shove up there later tonight or tomorrow. So essentially, what I've done is created a little bit of a project here. I'm dealing with plain TypeScript, for lack of anything sexy. I'm not using any sort of framework. This is about as bare bones of a TypeScript project as you can get, and it's not very interesting. But it roughly lays out the sort of things that you would expect in a TypeScript project. So using npm as my workflow assistant in here, I've essentially built a bunch of scripts that do my build, and test, and linting, and everything that I would expect, including my ci-test as well, too. I'm using Intern, which is the open-source testing framework that we developed at SitePen, and is available out there to run my tests in. TSLint you find more commonly used for TypeScript linting. ESLint is coming along, and potentially I believe they're trying to integrate to TypeScript services so they can get that type information to lint a little bit better. But really, right now, TSLint is probably the only thing that you can realistically use to get the sort of type insight and design linting in there. This is my tslint.json. There are quite a few sort of things that you can start to take advantage of because you're including types in your JavaScript. There are probably a lot of code conventions that you would want to make sure you're adhering to in your own code or in your team's code that TSLint sort of gives you the ability to control those and specify those. And my dependencies, in a TypeScript project, you see a few interesting dependencies. One of the biggest challenges is that if you want to use code that wasn't written in TypeScript, you know, just old JavaScript code, if you really want to get the benefit of it, you really need something that describes what that code does in TypeScript type. So that when you start to use it in your program, you sort of get the IntelliSense that you do in there. In this project, I happen to be using, mostly for infrastructure purposes, chalk for logging stuff to the console, Istanbul for code coverage analysis, and JSDom so that I can run my DOM tests under node without having to load them in a browser. But I also do load them in the browser as well. And so because I have functional code mostly in my test code that uses those, I want to have those available to me. So types, there's a whole set of a name space of NPM packages that are nothing but TypeScript type information. And here, I'm using TypeScript 2.2 in this project, and that's there. And all of these are sort of conventions that you can choose to do. I've then organized my code, and my source code is sitting in source, and I have my tests laid out here. I have both unit and functional tests and some support files. So this is what my ATS config looks like. You can run all of this on the command line. But the easiest thing to do to configure up your TypeScript is to specify a configuration file. So this is the sort of things that I'm using in here. What's happened is TypeScript has gotten more and more strict in its analysis of your code to keep you from making mistakes. On the other hand, a lot of those things would be breaking changes, things that would make your old TypeScript code not compile without errors under TypeScript. And so most of these new sort of more stricter analysis features are being put into the language as optional things that you add into the language. So for example, I've chosen and I've opted into strict null checks, which ensures that I'm not inadvertently accessing a variable that might be unassigned or a null value that I have to explicitly guard against unassigned or null values. I think everybody who's done JavaScript for any amount of time has made that silly logical mistake of assuming that a property has a value, and then somebody comes along and calls your function and doesn't pass you a value. And all of a sudden, you're dealing with, you know, a stack trace that says, "Cannot access property on if undefined." And so strict null checks will do that analysis and say, "Make sure that you've explicitly, for values that might be undefined or null, that you are making sure that they work, that you're guarding against them effectively." And there are some things that are more linting things, but they are included in TypeScript. Like that if you declare a local variable, that if you don't use it functionally in your code, that it flags it up, as well as if you access "this" and TypeScript can't reasonably figure out what "this" is and what type "this" is, you have to be explicit about what the type "this" is, so you're not sort of accidentally accessing "this" without noting to TypeScript what that is. And the same thing is no implicit returns, no accidentally returning undefined because your code block suddenly returns undefined. You know, if you've accidentally left off a return, you can return undefined. You just have to be explicit about it, instead of sort of accidentally forgetting things. And that's mostly what TypeScript focuses on. It really tries to focus on statically analyzing things that are likely to be errors. Sometimes those come into conflict with each other and TypeScript's had to choose what is a likely error and what is a sort of edge case possibility it might be an error to keep the usability there. No. So this is my exciting application at the moment. For those that aren't familiar with TypeScript at all, it allows you to adapt, adopt ECMAScript future standards. So, you know, it supports pretty much all of the syntax of ECMAScript 6 2015, 2016, 2017. Some of the 2017 syntax is already in 2.2, and even a few things that are still out there in the future, be they 2018 or beyond. Some of those things that are really not clear that they're going to become part of this standard, they're usually enabled under flags. Another thing, those of you that might be familiar with Babel, Babel, is TypeScript does not provide functional fills for things. It only provides syntactical fills for things. So it'll take a future standard syntactical construct and allow you to emit that down to a previous version of JavaScript or ECMAScript. But if you need something like, for example, I'm targeting right now ECMAScript 5 in my compilation, and if I wanted to create a copy of an object, right, I would probably do something like "const obj =" and I'm going to do some sort of literal "foo: bar". Okay. I wanted to make sure that I wasn't passing that as a reference, that I really wanted a new instance, because I wanted to pass that onto something. Or I was copying an object from something else, and I wanted to make sure that I wasn't implying that reference, I might do this, "obj2 = Object.assign" and I would do this. It's warning me of two things here. It's saying I've declared Object 2, but I'm not using it anywhere, which would be a problem. But I might use it in a second, so I could just do that and that problem would go... Oh, no. Object 2, and that problem would go away. So now, I'm using Object 2. But it's also saying here, "Property assigned does not exist on type object constructor," right? And this is because TypeScript is providing the syntactical fill. So I could do "for of," I can use "const" and "let," which aren't part of ECMAScript 5. But I can't use any of the functional things. But if I were to go into my tsconfig and say, "Actually, I want to use 2016," where that should be in my environment and hopefully this works so I don't have to close this. And therefore, that's gone away. So what's happened is TypeScript has taken all of the functionality that was added in TypeScript 2015, I could've done 2015, 2016, and added that in and made that available in my environment. So when it outputs this, it will output it assuming that the Runtime environment can interpret the syntax of 2015, 2016, or 2017. But this also shows one of the really powerful features of TypeScript, which is this sort of IntelliSense that you can get, very rich IntelliSense that tells you exactly what assign is going to do. The interesting thing, too, is that, because of the way assign is typed, it's taken both the type of this object and the type of this object, and combined those together in an object. Which you can see here is an empty object and an object with a property of "foo" with a string "literal". So it knows that there is "foo" when I go to my code completion, so I can access that. And anywhere I get that, I can get information about what that should be. And basic TypeScript is if I were to...we'll do a variable and we'll say "let foo:", but I'm going to say it's a number. I'm going to say that's a number, and now I will try to assign. It will tell me, "Well, wait a minute. The string is not assignable to a number." Again, because this is future syntax, all modules... Most TypeScript is written as modules, and it uses the ES6 module format, ESM. And so if I wanted to bring in, I've got a class here. I'm going to import "MyAwesomeClass" from "MyAwesomeClass". Yeah. Okay. I don't have an Internet connection. And it's really telling me that I haven't used this import, and I probably should. So I'm going to new up a...my... "const mything = MyAwesomeClass". But it's telling me, "Wait a minute. You're trying to... You can't call it as a function. That's not what that is. That's a class." And now it's telling me, "Oh, wait a minute. Your specification of what a "MyAwesomeClass" is, is expecting an options argument to the constructor. So I need to add that in. Okay. Now, it's telling me, "Wait a minute. That's still not good. You're missing something. You're supposed to pass in 'name'. 'Name' is a required property." And now, it's still not happy. Okay. What did I do wrong this time? I was supposed to pass in "type". Okay. So I'm going to go "blah". Oh, wait a minute. Now, it's telling me that "blah" is not assignable. That the only valid values for that... It is a string. But you can only assign it "foo", "bar", or "baz". So I'm going to call it "type: baz". Okay. And finally, it's stopped complaining about that, except for the fact that I'm not using "mything". So I will use "mything", but "mything" also has all of the properties and/or methods. So TypeScript allows you to declare properties. But it's a common thing that you often end up manually declaring on classes and JavaScript anyways. But TypeScript will take care of that for you. And so I have property "foo" and there's that. But let's take a look. So I'm developing this, and you know what? This is a bit confusing. I don't remember what I actually meant to do in MyAwesomeClass. I'd really like to take a look at that code. And when you have an IDE that's integrated with the TypeScript services, you can easily navigate to your code and do things like that. And you can see here that this looks like an ECMAScript class, except there's a few other things in here. One, I have visibility on my methods and properties. Two, I can actually declare properties in my class. And this is the sort of thing that you see quite common in TypeScript, which is the type annotation for something. This can be the type annotation for a property. You can also be explicit about type annotation and a function return. But you start seeing a lot of these types, and you become very familiar with them. If TypeScript can figure it out for you automatically, because, for example, I'm returning this ".foo" which is a my private thing in this accessor that I've declared there, it'll automatically tell you that "foo" will return a property of "string". You don't have to be explicit about that if TypeScript can do the static analysis for you. But where the problem came in with my options, you know, where I couldn't figure that out, was that I've got this nonoptional argument called "options" that I've put into my class here, and this has something that's called an interface in TypeScript. And an interface is one of the big things that's added in TypeScript, which is an abstract concept. It has no Runtime code emit. It is simply a description of a shape, a duck type of something, of an object or a function, that you want to be able to constrain in some fashion. Depending on the actual situation, you can have a super type of this, which has additional properties, and you can have subtypes, and there are other lots of type operators that are available in there. And you can see here, that this is where I had one of the relatively newer, possible features is what they call type literals. So these are string literals, which say that, "Instead of any string, I'm going to narrow down the strings that can actually be assigned to that." So I can have somebody interface with me in a little bit of an easier fashion, and then I have something here, which I didn't actually have to specify, which is an optional value, which has enabled boolean can be in there. The other thing is JSDoc is built into the IntelliSense. So if I hover over these... Oh, no. If I were to hover over these in certain instances, I can get the JSDoc added to it, so that I know what's going on. And so those all work in there, and JSDoc is sort of integrated into them. This isn't going to show up too well. But I'll just show you real quick. I'll just go in and run this package, which will be up there, npm test. And I'll just show you real quick that when you string all these things together, there's a lot of tooling out there already. If you use Angular, it comes with a set of tooling that kind of sort of works, which I think Guy is going to talk about. And I've screwed something up. Which is the thing... Which I'm not going to be able to fix quickly. The good thing is, it's telling you all the sort of mistakes that you make. Oh, I basically screwed up because I changed the target. Hold on. Let me see if this fixes it real quick. And my code wasn't actually... Yes, 2016 safe. I'll have to probably... Okay. I took that out anyways. All right. That's compiling, and it's actually running my test for me. I've got this in a situation where it runs my tests both through node and through the browser. And we'll come back and give me my code coverage. One of the biggest challenges, which is one of the frustrations that we had when we started building Dojo 2, was, "How do we get back to the original source coverage in this?" And we ended up just writing a little tool called Remap Istanbul. So we take coverage output from Istanbul. Hopefully, NYC and the next version of Istanbul will sort of make that unnecessary. But we're still in the situation where if we use webpack or we're using other transpilers like Babel in that we get code coverage for our emitted code. We don't get code coverage for our source code. So Remap Istanbul will take that coverage information, look up the source maps, and give you your code coverage back to the original thing. So that's integrated into this stack as well, too, and I can see at least for the two modules that I've got there, I've got full code coverage at the moment. Any quick questions? - [Man] I'm just interested [inaudible]. - We have benchmarked some of it, some of the ES6 features. Somebody did a really good article on sort of comparing transpiled ES features against native ES features, and some of the native ES6 and beyond features are ridiculously slow compared to the old way that we used to solve those problems. And we did some performance tuning in Dojo 2, for example, where what we ended up doing was backing out some of the ES6 constructs in our critical code, because those sort of modern constructs actually caused problems. And you also have to be careful of emitted ones, too. You know, sometimes what happens is you can write it in that, but when it gets transpiled, it transpiles into something that isn't overly efficient. The question was, you know, the performance and transpiling for the video. Yeah. But there's no perfect solution. It's a lot of hand-tuning at the moment. Intern, we've had an attempt at benchmarking. But again, it's so complicated to test unbundled code these days, or untranspiled code, and, you know, do all those sort of configuration and all those different permutations. That really, the approach that we've taken amongst the Dojo 2 team is, "Figure out where our critical performance needs to be, and then hand-tune those by, you know, using some sort of benchmarking tool." And again, there's a couple good articles somebody has been doing. I don't have it off-hand. But it's good out there, where they had a whole matrix of, "These are what the transpilers do. These are what the constructs do. This is what it's like under V8. This is what it's like under, you know, Edge and WebKit, and the different ones that are out there. And there's some real shockers. There's some real shockers. Because even some of the modern stuff, even on V8, they haven't gotten around to tuning it in some cases, and you'll get a tenfold decrease in performance. What sort of overhead do you have on [inaudible]? So, I mean, we write all of our unit tests in TypeScript. So the hardest thing for us... Oh, it's loading the jsdom. So a lot of it's through a test harness. If you have a test harness that has good TypeScript typings and you write all of your tests in that, you find that it's best to just sort of embrace the typings in TypeScript and work within the constraints of that. And actually, a lot of times when you write your unit tests, you'll find that you find a lot of maybe your typing logic errors as you start to do that. The one thing that is a little bit hard and there isn't a good solution is testing your types, right? So if you have an expected interface and you want to be able to say, "In a unit, I want to validate that interface on just type information," we've hand-built a couple things that we're not really happy with and there's no universal solution for that. So if there was an improvement in TypeScript testing, your unit tests just work like your functional, you know, your code, it sort of works. Right? But if you want to just test the types, there's some gaps there.