Building Familiarity with TypeScript's Syntax and Functionality with Orta Therox
Orta Therox, a former member of the TypeScript core team, shares insights into how developers can become more familiar and productive with the language.
Orta covers generics, compiler flags, the lib.dom.d.ts
file, and more while emphasizing the importance of consulting the TypeScript Handbook whe
Transcript
Matt Pocock: 0:00 What's up, wizards? I am here with Orta Therox, a TypeScript legend, ex TypeScript contributor, and TypeScript brain hiding beneath a flat cap. I'm extremely excited to talk to you about all things TypeScript, pick your brain about your mental models for all sorts of things, get you to talk us through some code.
0:20 You're one of the people that helped write the "TypeScript Handbook" too, so understanding your models for how you teach TypeScript and how you understand it on a deep level. I'm really excited. Could you introduce yourself to all the kids at home?
Orta Therox: 0:36 Hey, kids. I'm Orta Therox. I have a very long history in open source, starting with doing Ruby, and then moving to native iOS code, to that eventually turning to doing React Native, which is my introduction to JavaScript at scale, where eventually, after first making a flow code base and trying to contribute to the code base of Flow, I eventually realized that I just couldn't contribute to a Camel code base because the concepts are just too far away from the things I understood.
1:10 I started to devote quite a considerable amount of time to getting TypeScript up and running within our team. Then that eventually turned into a job working on the TypeScript compiler, where I spent about two and a half years working on documentation, and integrations with external systems. I started bootstrapping Svelte's documentation.
1:30 I started tooling for TypeScript, for example, and working on making it so that the TypeScript team was integrated into the rest of the JavaScript ecosystem in an elegant way by making it easier to integrate from Definitely Typed, and improvements to things like the DOM types.
1:50 Really, it was a little bit of compiler work, but a lot of outer integration systems. When I was interviewing, I pitched to the TypeScript team that I'm not a very good compiler engineer. What I can be is somebody that can work on the moat of TypeScript if TypeScript is a castle and the village that surrounds it.
2:10 TypeScript is trying to scale that project, it constantly requires rethinking about how to interact with the rest of the world. I was hired to work on that.
Matt: 2:21 That's interacting with other systems, interacting with developers, and interacting through documentation as well. I think a question I'd love to start you on is at a high level, how would you explain TypeScript to someone who's never heard of it or someone who's maybe heard a little bit of it but is a little bit skeptical?
Orta: 2:45 The tagline that we redefine TypeScript with is JavaScript with types. TypeScript's entire goal is explicitly nowadays to be only JavaScript with the extra type annotations that can be absolutely removed. If we take this massive language of JavaScript that people are using to build huge projects with and we apply a little bit of static typing so that people can be productive and get really good tooling.
3:14 We often talk about the types as being the key feature of TypeScript, but internally a lot of how we frame it is types for tooling. That is the types that we give you need to be able to power really useful tools in order for people to feel compelled that the advantages of using TypeScript are the tooling and the types are there to get you in that space.
3:35 Not necessarily that, "Hey, here's all these really cool type features." It's more like these type features are here to give you the tooling. JavaScript with types and types for tooling are the two very quick ways of summing up how I would pitch TypeScript to people.
Matt: 3:52 Interesting. A lot of what I'm focusing on with Total TypeScript is taking folks from a beginner level with TypeScript into places where they can build their own abstractions, where they can feel confident with understanding the flow of types through their system, and especially working with generics.
4:14 There's a lot there in that topic. How would you explain generics to someone who'd never heard of them before?
Orta: 4:24 I think generics are pretty easy to evolve when you think a little bit about how in variables work inside normal expressions, normal code, if you will. Generics is a way of providing arguments to some other type. You might have an array type and you want to say what is inside that array type. You'll add what is effectively a parameter to the array that says, "Hey, this is the thing that goes inside it."
4:53 That, to me, is usually the first way of introducing generics to people as being, "You already know there's a variable associated with this, but you call it an array of strings and your variable name. Why not take the of strings and put it into the type? Now the type says, 'This one generic parameter ensures it's only a string on the way in.'"
5:14 Defining it as the inputs is usually the first way to think about how generic arguments exist in TypeScript. Once people feel confident with that, then you can start talking about, "Hey, the generics can now affect the output, right? You can pass this type in. It gets transformed a little bit. On the way, it comes back out."
5:37 You can talk about how your array type of string now is used, that you only put the string out on the return value. That is the nice way to teach it, in my opinion. You first say, "Look, this thing can be used as an input." Then later you can say, "This thing can be used as an output." Then when people are really wanting to understand it, then they're, "How do I do parameters?"
6:01 I don't need to add, "By having defaults," or, "How do I constrain those parameters?" That's when things get more complicated, obviously. It's an incremental way to learn. A parameter is simple. Then slowly you learn more and more of these features of generics that get you to more and more powerful positions but at the trade-off of more and more complexity.
Matt: 6:22 Why don't we get straight into a bit of that? I think that's a really nice lead-in to start looking at some code, if that's OK. I've got a function here, which is object keys. What this does, it basically says, "Imagine we have type object to array of keys. We have a type of obj, or let's say, result equals object to array of keys." You pass in a 1B2, for instance.
6:49 What you're going to get out of this is an array of these keys, which is A or B here. Then object keys down here, it basically does the same thing, it maps the type helper onto a function. When you call it, then you get object keys. Let's say, we call this at runtime A1, B2. How would you describe the difference between these two constructs?
Orta: 7:20 I think two of these are basically type level only. One of them is value and type constricted together is maybe one way to frame it. The way that I would generally think about that first one is that it's very good at describing existing JavaScript, whereas the second implementation is very good as being the actual TypeScript implementation of how you would build something like this.
7:46 TypeScript has the intention of...it has to describe JavaScript. That's its goal. You also want to be writing elegant TypeScript while you're doing something. Wait, do you know what? Let's start this section again. Let me dig into this question again in a way that I think...What are you trying to get towards? These are style choices from my perspective.
Matt: 8:13 Interesting. Style choices, in what sense?
Orta: 8:19 One of them exists inside the type system entirely. The type object keys...It says that...[reads quietly] the array of those. Then that's just using it. The one on the bottom is just defining it again at runtime. What is the difference between the type of than that you're just declaring them?
Matt: 8:47 I'm trying to get to an idea that one of these, this first one, as you were saying, it's basically, you just pass in a type as an argument. There's no inference happening here. Whereas, in this one, when you use these type object keys here, there's this inference happening. You're not actually having to pass the parameter in. You can pass it in. You can pass it in a number, B number.
Orta: 9:14 Yeah, sure. I see what you're getting at. The interesting one is that the first one is really just an ephemeral type. We can start that again if you like because I definitely was hitting in those directions, but I would definitely have veered off in a different way.
Matt: 9:31 Cool. Let me do that again. Let's start looking at some code here. We've got an object to array of keys here, which is like a type helper. It's saying, "We're going to take in a type into here. Then we're going to return an array of key of T." When we pass in a 1B2 into here, then we end up with A or B here.
9:57 Then this here, this object keys is a function which does the same thing but it's mapped onto a function here. This means that you don't actually need to pass in any types when we use it. It's basically just, we infers it from the type of the thing that's passed in. I'd love to get your thoughts on the differences between these two approaches and how you would teach this yourself.
Orta: 10:28 Some of the key distinctions between both of these is that the first two examples, like the first two types only live in the type system entirely. In theory, you could reuse those in different contexts and in different places.
10:41 That talks to how TypeScript exists to describe existing JavaScript and things like that. The second one is probably how you would be writing this in everyday code. You're making it, so that it's actually code that can be run by JavaScript at the end of the day.
11:06 The usage side of the API would mean that it is automatically inferred, simply because when an object literal is passed initially into a function, then that will get passed in as your T. If you do not pass that object literal in first, then it will be unnarrowed, and you would actually lose a good chunk of the initial inference that you were hoping for.
11:37 By having the type parameter set up inside the function that will actually happen in JavaScript, you end up having to write less type syntax in order to get more TypeScript typeness, but without actually having to write the extra code to make the TypeScript code do the thing that you think it should do.
12:00 A lot of the time, you're trying to make TypeScript work how you think it should work according to the JavaScript, because you understand the JavaScript, but you need to make TypeScript understand the JavaScript. By attaching it directly to the evaluated code, then it is closer to the place where it should be working. Therefore, you get inference in a way that you expect, not necessarily in a way that you need to set up.
Matt: 12:23 You've like to take this all the way back, then. Imagine if you just take all of the clever type annotations off this, and you've got a function that's basically just wrapping object or keys. Then what you end up with here is just string array here.
12:39 By adding all of this clever stuff, you have an opinion about what that type should be. You're getting TypeScript to infer this type here from the thing that you pass in. Then, you're annotating extra stuff on top of it, basically mapping the type helper onto the function. Yes.
Orta: 12:58 There's two ways of doing it. They're different. One is reusable at a type level. You can use object to array of keys and other functions now if you wanted to, but object keys is really only useful if we're in the context of object keys.
13:18 There's a TypeScript feature that came out in maybe TypeScript 4.6 that allows you to set up an object keys, like the instance evaluation with a set type ahead of time.
13:32 You can actually reuse that object keys with a set type, with a single generic parameter, and then later reuse it. Realistically, those one is used for reuse in the first case, but then the second one is used for...You're only going to have one function called object keys in a code base realistically looking at that. That's probably how you probably should be building it.
Matt: 13:58 Got it. That's really interesting. What problems do you feel that this kind of generic code is designed to solve? What would TypeScript look like without generics?
Orta: 14:14 I don't think you can't make a type system that is as complicated and feature rich as TypeScript about generics. It's just like, as a structural type system, which means a type system that checks every single field to see if they match the other thing that it's been compared against.
14:34 They just come to a point where you need the ability to reuse fields in some way. If you didn't have that, your types would be incredibly long and incredibly large amounts of overlapping code between each other. Generics gives you that reusability that is essential to build any complicated type system.
14:58 I say that, but I used Objective-C for about 10 years, and it does not have generics. It has a thing called lightweight generics. We got by, but you needed a type system in that case that's nominal, wherein every single class has a unique type that if you compare a type of an animal to the type of a dog, those two are always different. Whereas in TypeScript, they could be the exact same, and they could be treated the exact same in different cases.
Matt: 15:28 Got you. Let's use that as a segue to dive into talking about TypeScript's sort of....I don't know what you'd say, structurality. It's a structural type system, right? Not nominal?
Orta: 15:40 Yeah, yeah.
Matt: 15:41 I've got a little sample here, something that I was working on for an article recently. I'm really interested in your thoughts about this error here. Let me actually just print this error out. Now, if I pull this out here.
16:00 I'm interested by this error, because what's happening here is we've got an example function at the top, which is a type. It's declaring that it's a function with something, with awesome being a number. I've actually made an error here where awesome is 1. If I change this to be a number, then the error is going to go away.
16:21 Now, I'm really interested by the structure of this error and what this tells me about what TypeScript is doing under the hood structurally. I'd love you to explain that at a deep level, if you could.
Orta: 16:34 Yeah, sure. TypeScript is a structural type system that is literally where we should start this conversation. What a structural type system means is that basically whenever we are sort of type checking, what we're really doing is checking if these two objects can be assigned to each other.
16:55 Can exampleFunc be typed to be called exampleFunction? That check actually happens where you are seeing the red squiggles right there. The way that type checking works at a very high level is that, first, we create these things called abstract syntax trees, which are an in memory representation of a source code.
17:21 TypeScript iterates through all of the sort of root nodes of that tree, which are basically what we call statements. Each statement is a line of code. We'll echo that a little bit, but effectively it gets to a point...
Matt: 17:34 This is the statement. This is the statement. This is the statement. This is the statement.
Orta: 17:38 Yeah, you have statement on that page, basically. We go in and we look at this identifier, right? ExampleFunc on the third statement. It is declared into TypeScript by that colon that is going to be this other type. TypeScript will look at those two and say, "I would like to look at the structure of the results of running this equals sign on the other side to the types that you have defined after the colon."
18:08 We're looking at, is example func the same as the function definition that is available directly after it? It's those two things that are being compared. What TypeScript will do -- again, I'm waving my hands a lot for a lot of this, but it's good enough -- but this looks, "This thing that we've got here that you have selected, the function, is that even a function? Is it function-like?
18:31 The definitions of functions in JavaScript are more ambiguous than you would probably expect, but we have flags on these different bits of objects that says, "This is a function-like, so it would be acceptable to go in there."
18:48 They compare the functions, and they say, "They're both functions, so we'll go in." Then it compares the arguments, "That's very complicated, so I'm going to completely gloss over that," but the return values of those is very simple. There's only ever one type of return value, and you can compare return values against each other.
19:08 It is now starting to compare, does the return values' structural match with the structural match of the types one? It will look at "something," and then it will look at "excellent," and then it will look at "awesome."
19:21 That's why when you saw that error, you started to see this breadcrumbs at the bottom when you had the full-on. Where it was like, "Something.awesome are incompatible with these types." The first line of the final part of the error message.
19:35 What that's doing is effectively looking...We find out where the break is, where the types cannot be assigned to each of them, and then keep going back and seeing which parts are broken. That eventually turns into the breadcrumbs that you're seeing in there.
19:49 TypeScript has to go all the way down to find the part that's definitely broken, and then it starts going all the way back up to say, "What is the closest path to an assignable type?" What parts are assignable? What parts are not assignable? You keep narrowing down until you find the bit where they do not assign.
20:07 That's where you start your error messages, and that's where you give you breadcrumbs, and then you eventually go back to say, "What was the definition? What was the part that said these two things are the same?"
20:19 That's where you give your error message. That's where your error message lives, up here on exampleFunc, but your actual error, per se, lives down at "awesome, 1."
20:28 Like if TypeScript was going to give you infinite squigglies and had different definitions of how we describe them, then it would be like awesome would be a bit that would be highlighted. What about this idea? I'm throwing it out there, just at this moment.
20:44 If you highlighted that exampleFunc, if your cursor was there, why not show a cursor underlines on all the related, we call them spans, all the related spans for where error messages should also be. Your mouse should be there, so awesome should also be highlighted now because that's where the 1 error message is. This is all under the block of what we call assignability bugs.
Matt: 21:12 Got you. What you're saying then is each line of this error message it's as though it's walking down the tree each time. It's like a stack trace, right?
Orta: 21:23 Yes.
Matt: 21:26 Because if we change this slightly to say, imagine instead of this being an ExampleFunction, we're actually just going to type the return type instead, ExampleReturnType, and then return it here.
21:41 This is a slightly different case because we're not saying the exampleFunc is the thing that we care about in terms of assignability. It's now this that's what we care about. Could you elucidate the difference here?
Orta: 21:59 At this point, it doesn't matter what the type of the actual function is. It only matters the type of what the actual return value is because that's the only place where we're actually telling TypeScript, "Hey, these are the assignability rules that I'm assigning to this bit of code."
22:13 You can actually see now that the first time that there is actually an assignability break is down at awesome instead of actually up at the return type. You're getting your error message down here because we know it's only at one place that we don't have to go further back up the tree to give you a 1 idea about where it is.
22:33 There's a good argument that both of those could have an error message. At the same time, if you know exactly where it's broken and it is exactly in one place, there's not much need to separate out the error message up here and the error message down here. You know exactly that it is awesome.
22:50 This can be a little bit tighter on the error messaging to tell you exactly where your error message is rather than tell you this was your assignability problem, but this is where the actual error message is.
23:01 In moving the error message, we have a tighter type system now in theory. Obviously, the function bit not being there does make it weaker in other aspects, but your error messages are a bit tighter simply because you've added more annotations at the right points.
23:16 Sometimes when you're working in very large code bases, trying to figure out how to have less TypeScript but more explicit in the right places can often lead to extremely good error messages in comparison to giant trees of like, "This bit to this bit to this bit to this bit are not assignable to each other."
23:35 That is definitely an art form that you really need to see a lot of error messages to learn where the right things are for that.
Matt: 23:44 Absolutely, because it's doing the assignability checkup there. It has to not only figure out the cause, but build up like a legal case to say, "This was the cause, this was the cause, this was the cause," going all the way down. The closer you can get to comparing an object to an object, I guess, is pretty good, whereas a function to a function is less good.
Orta: 24:06 A really good way to expand on this is I've been writing my own GraphQL APIs for a very long time, and people tend to build these GraphQL API DTS files generators that cover the entire graph of your GraphQL API.
24:24 If you don't know what those are, all I'm saying is making giant DTS files that have a very large amount of generics inside them. You get these error messages that are incredibly long, because it has to reference a ton of generics to eventually get to a single name of the query you're making.
24:41 A quick rewrite of some of those DTS generators, which I did recently, that just do exactly the objects that exist, the error messages went from being seven or eight sets of nested things deep to just being one, and your error messages became trivial. It required saying, "Hey, I want a simpler set of DTS files," even if there's more text there.
Matt: 25:03 Fascinating.
Orta: 25:04 It's a constant trade-off.
Matt: 25:06 This takes us into an interesting discussion, which is another thing I'd love to touch on with you, is around interacting with external libraries and external DTS files, because this can be really painful and can generate some enormous errors.
25:23 Another thing I want to focus on with Total TypeScript is giving people the confidence to explore all of the generated d.ts files that ship with TypeScript that come from types node and exploring those. I'd love you to give us high-level overview of how that works with TypeScript, like where DOM typings come from, all that stuff.
Orta: 25:48 I'd say there are roughly four spaces where types can come into your projects. There is the types that get shipped with TypeScript. We would refer to those as lib.d.ts. There's the DOM types, which .dts. There's Definitely Typed. Then there's included inside your either code base or your node package manager, node packages that already have .dts files.
26:18 The first ones are the lib.d.ts. Those lib.d.ts exist to describe JavaScript. It doesn't exist to describe a particular library or anything like that. It exists to say, "Hey, JavaScript language spec 2015 had the map and set classes."
26:41 Somebody has to ship those to describe that type system further down the line. That is what target does for tsconfig. It defines how far in the lib.d.ts do we add these types into your type system.
Matt: 26:54 Could I interrupt you so we can try that out?
Orta: 26:57 Please.
Matt: 26:57 That would be great, because what you're saying is, let's say, const whatever equals new Map. Going from this, how would I say, "OK, I want to see where this d.ts file is coming from"?
Orta: 27:14 Are you on a Mac? You could command-click on that, and it should show you, or control-click.
Matt: 27:21 There we go. Here it is. We're now on lib.es2015.collection.d.ts, which is interesting. This is one of the many, many files in here that represents all of the stuff that JavaScript can do.
Orta: 27:37 Exactly. Every single one of those is either a spec or a feature collection that's very explicitly about describing the JavaScript spec and nothing else. This one, you got WeakMap on screen, as well as Set under there. Those are things that were added to JavaScript in the 2015 version of JavaScript. Each one of those files represents a subset of that year's language.
28:06 If you've got ES2017 installed, then it also includes all of ES2015, etc. The target definition in your TSConfig defines what is the year that you start, and then it goes all the way back in those. ESNext. That's all of them. That's the specs. We're just saying all of them.
Matt: 28:26 If I change this to ES3, then I'm going to get a ton of different errors because, let's say, map might not be included if I restart my TS server.
Orta: 28:38 If it doesn't, there's some magic going on.
Matt: 28:40 Of course, it didn't go the way we wanted to.
Orta: 28:51 That gives TypeScript the way to have a lot of these compiler flags that allow us to say, "Hey, you only want these bits." That's its only goal. Nothing else is included in that. That is just describing the spec of JavaScript.
29:07 A realistic take on what people do with JavaScript is to say, "Hey, most people are working with Web and JavaScript," especially TypeScript is 10, 12 years old at this point. At that point, that was the only use case for JavaScript especially. Everybody wanted, it was a very reasonable idea and it still probably is, to include dom.d.ts as a default.
29:31 Lib.dom.d.ts is pretty complicated project that outputs a single DOM file and that small iteration file that you saw next to it. This one is a separate repo from the TypeScript repo. It is maintained by an outside collaborator who works at Mozilla, and is constantly up to date with spec changes in Web browsers.
29:58 The problem with this one is, realistically, you don't want to version your Web browsers in the way that you might for your language. There is some advantages to this. In the Babel world, you'll often see people say, "I want to use this version of a minimum two years support" and stuff like that. TypeScript updates once a quarter.
30:20 Every single version of the things we ship ship to everybody. There's a lot of incentive to just only do the latest. Our rules for this are, if a feature is in the Web platform in general, and you can see it in at least two browsers, that's Firefox, Safari, or Chrome EOMs, it has to be in two of those to be allowed in.
30:49 What we really do is we look at the database behind, it's called, can I use this, which is a website that lets you say, "Hey, can I use this CSS feature? Can I use this JavaScript feature?" We pull data from that and documentation and turn that into a giant DTS file. That is constantly updated. We get notifications about new updates all the time. This always represents the latest version of the DOM.
31:19 One of the few big TypeScript features I shipped was the ability to have a custom version of this and just you have a custom version of any of your lib.d.ts files as well. That can be overwritten by your package manager. It's a rare feature that people need. When people need it, it's there.
31:38 Some people want a different version of the dom.d.ts. Our rules of accepting something that's on two browsers, in my opinion, helps keep the Web a little bit more equitable. For a lot of people that are building Chrome-only, which is massive, they want a lot of extra APIs that are just not available in here, or not described in here, more importantly.
32:01 They exist in the runtime, but they are not described in this dom.d.ts. It gives them the ability to say, "Hey, I want a Chrome-only version of this dom.d.ts, which gives me my APIs." Whereas we still get to say, "Hey, we're doing the Web platform, not necessarily the Chromium platform."
Matt: 32:19 By default, then, we have the first one, which is all of these extra describing JavaScript stuff. Then in here, we have the describing DOM. What was the third one you said?
Orta: 32:30 Definitely Typed. That is describing code which is in JavaScript, but no one has really turned that into TypeScript code.
32:42 The way to think about that is those last two are, is it a code base that is made in TypeScript that generates DTS files or is it a JavaScript project that is not got types and we don't want to annoy the authors of that JavaScript project by giving them the types to try and make them, because some of these types are complicated.
33:01 These types are complicated. The React types are complicated. The React team don't use TypeScript. There are a set of absolute heroes that try to map this very complicated system of React into a runtime that can exist inside the type system.
33:24 It's not fair to call it third party because the React team are very involved, because if this doesn't accurately respect their thing, their runtime, it will absolutely cause them loads of pain because they'll get all these issues. They try and make sure that when they introduce hooks, there was type definitions for those straightaway.
33:47 Some library authors do that themselves. The React team allow it to run on DT. Running on Definitely Typed means that there is an authentication system in place, there is checks that it is not broken. It gets verified against TypeScript. A TypeScript compiler on its test system runs the newest versions of TypeScript every day against every existing Definitely Type project.
34:12 You know that we are not breaking Definitely Typed in a feature that's new to TypeScript. They regularly break that, in the sense of we regularly know we've broken that and can get back into a green state really easily because we have all that checks. That's because we have this library of extremely complicated types used to map complicated JavaScript into a type system.
Matt: 34:39 This is where when you say, "OK, I want to use this library, this abstraction," you say, "I want to bring all of these types into my project." How important do you think it is for folks to understand the complicated syntax that's happening here?
34:57 Just to understand this function, you need to understand function overloads. You need to understand generic constraints, passing generics to other types, tuple types. How important do you think it is to understand this stuff?
Orta: 35:10 I don't. You need this further down the line, but only when you're...My framing of this is that you have application-level TypeScript, you have utility folder library TypeScript, and then you have describing really complicated JavaScript TypeScript. Those are three separate phases that you could grow into over time.
35:38 When you see these proper TypeScript nerds doing really clever TypeScript, they've spent a lot of time trying to describe existing JavaScript. That's how they've learned all of these concepts.
35:49 I think that that is a spectrum that you start off just maybe making a map with a string as its parameter, but eventually -- and that's application-level TypeScript -- and you make something a bit like the types that we described earlier with generic parameters, extended something. That's library-level TypeScript.
36:09 You're making a generic function that's reusable anywhere inside your code base, so it needs to have a bit more TypeScript complicated stuff. Yeah, this one. You have to learn some concepts probably to start writing code like that if you've just written TypeScript as an application developer for a while.
36:29 Then if you want to start describing some of these really complicated things, then you really do just have to sit down and go through first a handbook.
36:38 One of the best ways of learning that is we did the series called...they were Halloween-themed playground exercises. [laughs] Did it two years in a row. They were good. Their goal is to actually teach you to get towards that types. By doing it incrementally by, "Here's how so and so works."
36:59 Eventually, we force you to read enough documentation to eventually complete some of these challenges. That's what type challenges are useful for, to get from this to the hard stuff.
Matt: 37:10 Definitely. That's the structure of Total TypeScript as well is building, basically. These challenges just progressively getting harder and harder. You said there was one-fourth thing, this one-fourth way that you could...
37:22 [crosstalk]
Orta: 37:22 People ship their own types, right? Any library that's got TypeScript, that's made in TypeScript will ship its own .d.ts files attached with them. As of what now feels like an ancient release, with TypeScript 3.7 allows you to use JSDoc to generate .d.ts files. Yeah, exactly, that's a perfect case. Vite UI ships with the JavaScript and the .d.ts.
37:49 That is largely the traditional way in which TypeScript says, "This is how you should define your types. We're not shipping TypeScript to clients. We're shipping JavaScript to clients. Separate from that, there is a definition file." This is an established pattern if you see it uses this pattern where you have a header file and you have an implementation file.
38:12 It's very traditional in terms of that setup for describing frameworks and libraries.
38:22 If you don't ship the .TS files, then TypeScript will just say, "I don't know what to do with this" in a TypeScript project. If you're in a JavaScript project, it says, "Hey, I'll give it a shot and see where it goes." It does an OK job, [laughs] but it's a complicated problem, especially if people minimize JavaScript.
38:39 That's really hard to infer what the types are or infer what the return names are and things like that. It tries. That's what you got to do.
Matt: 38:49 It's a tough problem. Let's finish up then by talking a bit about...Focus gone. Something's going on. Let's finish up by talking a bit about TypeScript's future and potential places TypeScript could go. I think that's a hopeful note to end on. There's a TC39 proposal to bring types as comments, which is interesting. You're interested?
Orta: 39:16 Yeah, it was one of the last things I worked on. Types as comments is a really cool idea because I think of it as a way to de-risk the JavaScript ecosystem from the problems of TypeScript being so popular. TypeScript is incredibly popular.
39:39 [laughs] For modern front-end programming, especially React Native, 100 percent TypeScript, more or less. Most people doing serious work are doing it in TypeScript nowadays. That is a serious risk to JavaScript. TypeScript doesn't want to be in that position.
40:03 The team doesn't want to be the arbiter of what JavaScript is and isn't. The team very explicitly wants TC39 to be that and the TypeScript team to be a collaborator with TC39. In part, Microsoft has the reputation for embrace, extend, extinguish. The idea of idea that you take an idea, you say, "Hey, this is great." Then you eventually find a way to negate it.
40:31 There is a culture of making sure that never feels like it's happening. The biggest one for me, for why I wanted to work on this is that types as comments allows other people to exist inside the tooling space and the language runtime space that is currently absolutely dominated by TypeScript.
40:53 Basically, the key definition for what the proposal proposes is that we declare areas of syntax where more or less anything can happen.
41:04 As long as it looks and feels a bit JavaScripty, then it will probably work. What that does is it allows tools and runtimes to be able to define areas where they don't have to do any syncing.
41:19 Right now, if you want to build Vite, you have to build Vite with support for TypeScript, because everybody's going to be throwing TypeScript code into it and it's expected to handle it, but Vite doesn't care about the types. It's not doing a type checker or anything.
41:35 Vite should really care about taking that JavaScript to be able to run it. That's generally, largely its goal. If we have a version of the language where the types are declared to be ignored at set spaces, then you don't have to think about TypeScript.
41:54 You just know to ignore that area of code and TypeScript support is a matter of adding the .ts file support. Eventually, this is me extrapolating, but it would be very reasonable if this proposal passes all the way to start seeing a move away from .ts files and be .js files.
42:16 The TypeScript checker would be checking these JS files with annotations that are TypeScript compatible. That would leave us into a road where JavaScript is once again, the language we mostly are working in. TypeScript now almost exists entirely as tooling.
42:38 When we say types for tools, that is part of what we mean about one of the end goals for TypeScript. It's not to undermine JavaScript to replace it with this new language. It's to give better tooling and to allow scaling of codebases that would be much easier to do if it was actually purely JavaScript.
42:56 That's why it comes up regularly at the moment that people talk about enums in TypeScript and how they don't like them. I think even you had a video on it recently. TypeScript were not [inaudible]. It's a useful tool, but it's not the tool that TC39 have said is what enums should look like.
43:13 It's the same problem. TypeScript doesn't want to be in those spaces, where it's adding new features to JavaScript. It wants to be in spaces where it's describing existing JavaScript. Types those comments actually gets us closer to there and de-risks JavaScript from TypeScript's dominance.
Matt: 43:29 That's so fascinating that you say it like that. It sounds like, for you and for the TypeScript team, .ts is an implementation detail. The real goal is to make JavaScript better and make it scale better and make the tooling better.
43:46 That's something that I noticed Anders said quite a lot. I was watching a lot of interviews with him. It's like the tooling was crap at the time for JavaScript. They wanted to improve it and having a type system means that you can then take that and push it into tooling all over the place. That seems like the central goal of TypeScript.
Orta: 44:06 Exactly. I was on a call with Anders last week and he still was saying that.
44:11 [laughter]
Orta: 44:11 He's been saying that for 12 years. Yeah...
Matt: 44:12 Well, it's still crap or like it's...
Orta: 44:16 [laughs] No. I mean, there are other people doing it, but we're the only ones that are really doing it. If you get what I mean. You can't replace TypeScript to VS Lin.
44:27 You could replace TypeScript with Flow, but Flow is at a point now where it understands that it wants to be only working on the Facebook codebase. You can use it, but that might not necessarily give you what you're looking for.
Matt: 44:40 Yeah. I saw State of JS came out today and Flow has dropped right off a cliff. It's somewhere TypeScript is dominant. That's fascinating. What do you think about the features that TypeScript added to the language like enums, namespaces? I imagine where types as comments, TypeScript's goal certainly now is to cleave very closely to JavaScript, right?
Orta: 45:05 Yeah.
Matt: 45:07 Obviously, they would never deprecate enums or deprecate namespaces, but if JavaScript gets it...What do you think about that whole mess?
Orta: 45:16 Oh, are you implying, are you leaving me somewhere?
45:20 [laughter]
Orta: 45:24 Of the whole situation what it is?
Matt: 45:26 Yeah.
Orta: 45:30 I guess I got two angles for this. The first is, those decisions were made a decade ago and those decisions were made in a completely different environment. I agree with the decisions they made at the time, especially considering some of the contexts in which they were made. Maybe a good way to frame that is a heart.
45:47 The second part is a concrete example. Decorators have been experimental in TypeScript for about eight years. They were initially added because the Angular team and Google were thinking of making their own programming language called AtScript, which was going to be like TypeScript but have decorators.
46:08 TypeScript and the Angular team came together and said, "Well, if we add decorators to TypeScript, would that be enough? Then we only have one language between those, and we're not trying to step on each other's toes." When Angular came out, there was like, "Hey, we're working with TypeScript to make these decorators in there."
46:30 Eight years ago, we guessed what decorators would look like if it was added to JavaScript. I'd say it hit about 80 percent of the mark. Now the question is, is hitting 80 percent of the mark good enough? That's the biggest tension of using any of these features is those were guesses from a very long time ago that were very reasonable and very well thought out, but that doesn't mean they were right.
47:00 Namespaces was wrong. It was not where the ecosystem went because eventually, we got a 1 module boundaries through import, exports, and the concept of a module script type in the spec. Then, for enums, the proposals for enums do take the TypeScript one into account, so it's very unlikely that they will truly break all your code when and if it does come out.
47:33 Using those features personally move you away from being aligned with JavaScript. I will always recommend people to not use either of those features. Namespaces, you have to use for working indefinitely type. That can never be erased in any way. You can more or less get the same thing out of using a const and as const feature with objects.
48:01 That's usually enough to warrant saying enums, you could probably live without in most code bases and they don't necessarily give you that much of an advantage over just using the primitives in JavaScript.
48:12 Namespaces can be impossible to replicate in some cases because there's just features in there that just don't exist anywhere else. No one else is trying to add types to lodash and things like that. The features of namespace is built to do that sort of thing, too. I recommend against them.
Matt: 48:34 This has been an amazing conversation. Thank you so much for diving deep into code and me showing you stuff although you weren't prepared for your answers so eloquently. This has been so fantastic to actually meet you and talk to you as well. It's been brilliant.
Orta: 48:50 It's been a pleasure.
Matt: 48:51 Thank you. Awesome. Thank you so much, Orta. Where can people find you?
Orta: 48:57 I'm a Mastodon person nowadays. I've joined the cool kids in the Vue crew of Web tools, so web2.ls/orta.
49:10 [laughter]
Orta: 49:11 Yeah, I know. I feel like as an old-school open-source person hanging out with all the Vue kids, I'm like, "Wow, you guys are doing awesome work. I just don't understand what you're doing anymore."
49:23 [laughter]
Orta: 49:25 My Twitter will redirect people there.
Matt: 49:27 They do seem to be really young, actually, and extremely enthusiastic and really nice as well.
Orta: 49:33 They've got the energy that you only have in the first five years of open-source content. After that, you get really like, "No, I'm not doing that." [laughs]
Matt: 49:41 You're 12 years in, right? Yeah. You must be off a cliff now.
Orta: 49:44 Yeah, or I call it defensive open source.
49:47 [laughter]
Matt: 49:47 Lovely. Thanks so much for this conversation. I really appreciate it.
Orta: 49:53 Nice. Chao, folks.