Let's Learn TypeScript!
Links & Resources
Click to expand the full transcript
Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.
Jason: Hello, everyone. Welcome to Learn With Jason, the first show of 2021. Welcome back. I hope you had a great holiday season. Today on the show, we're bringing Orta Therox to the show. Thank you so much for joining us today.
Orta: Thanks. Totally a pleasure to be here. First of 2021.
Jason: Yeah, so we've known each other for a while. We've kind of run in the same circles, but we haven't had a lot of opportunity to really collaborate. I'm super excited today to dig in and learn with you. So for those of us who are not familiar with your work, do you want to give us a bit of a background on who you are, what you're up to?
Jason: Nice. Yeah, I actually didn't know you had done all that stuff. That's very cool. So, yeah, I am really excited to dig into TypeScript. I have done an episode on TypeScript before with Ben. But that was kind of -- it wasn't like an introduction to TypeScript. It was sort of let's dive in and just kind of mess with it a little bit. So I'm really excited to go further here and get people introduced to the idea of TypeScript and what it's capable of, right. I feel like what Ben managed to do was he whet my appetite. He showed me how much easier my life could be if I were to go all in on TypeScript.
Jason: But I will say, I have yet to convert anything to TypeScript. So, I'm wondering if you can be the one to push me over the edge here. (Laughter)
Jason: You might not be writing TypeScript, but you are using TypeScript. Yeah.
Jason: And it's really interesting because -- so, one of the big complaints you'll hear when you talk about something like TypeScript is that you're adding complexity, but you can still do something like shoot yourself in the foot by declaring everything is an any type.
Jason: I got you.
Jason: But with that being said, I feel like we could talk about this all day. What might be fun is to dive in and start writing some TypeScript. So, I'm going to switch us over into pairing mode. Oh, my god. I just broke everything.
Orta: Mix tape.
Jason: What did I do? Oh, man. We're in so many of the bad places. Give me just a second. I switched over my whole scene to put us into -- okay. So here's what was supposed to happen. We were supposed to bounce over to here. There we go. Oh, no. What's going on? We've got chaos. Absolute chaos. Okay. So, what I did over the break, everybody, is I rewrote -- there we go. That's better. I rewrote the Learn With Jason overlays. So they are now a toast site, which I'm excited about. But --
Holy buckets, did that just work?
Yes! Did you see that work?
Orta: Was that the subscriber?
Jason: So, that's a sound effect. The sound effects work. I've got custom overlays working now. I also brought back the stampede with music. So, subscribers, you can do terrible things now. So, anyway, all that being said, and destruction aside, today -- usually I would be showing you the Learn With Jason live page. I broke that with the new version of Learn With Jason. So instead, we're going to look at the actual direct link to the live captions. I'm dropping it in chat because it is too long to say out loud. But, what this will do is we have Rachel today from White Coat Captioning, live captioning the entire show. You can see that happening on screen right now.
Jason: I love it. It makes me so, so happy. That is in an effort to make the show accessible to everyone, not just to people who can hear or who, you know, like if you're a non-native speaker. The way that I chatter at a million miles an hour is unintelligible to you. This is potentially helpful.
Orta: I don't envy you, Rachel. (Laughter)
Jason: Yeah, thank you so much, Rachel, for your help. And what this also gives us is this is made possible through our sponsors. Netlify, Fauna, Hasura and Auth0 all kicking in to make this show as accessible as possible to as many people as we can. But yeah, and next week that will all actually be working on the Learn With Jason site. That's currently busted. I did something wrong. I'll figure it out later. Make sure you go and follow Orta on Twitter. He has the enviable position of having his first name as his Twitter handle. Make sure you get that follow in. And we're talking about TypeScript today. Let's grab the TypeScript home page site. I see we're going -- there you go. Rainbow beard. Way to go.
Orta: Oh, nice.
Jason: Yeah, I know, Prince, you also have the first name Twitter handle. Not all of us can be as cool as the two of you.
Orta: I think I started Twitter in like -- 12 years ago or something. Like year one.
Jason: Oh, here we go.
Orta: Oh, stampede.
Jason: It's back! This is just a lot of joy in my life, that that's re-entered the stage. Is that stampede going to disappear? It sure did. (Laughter) All right. I'm really excited about this. I just kind of shipped this and we're going to hope for the best and see what happens.
Orta: It'll work out. It'll work out live. (Laughter)
Jason: But yeah, so -- oh, man. I'm so excited. All right. Now, we have -- we've gotten through all the -- indeed, we will do it live. Okay. I think we're ready to write some code. It's code time, right?
Jason: That's really cool.
Orta: Yeah, after I rebased it as well, got rid of that even. It was like 100% typed, just without any effort at all.
Jason: I was just going to say, I think I have a package we can work with here. Just double checking I didn't -- I already did TypeScript on this one. Dang it. All right, fine. Trying to think of one we can use here. I think maybe get share image. Does this one have types in it? Let's see.
Orta: I don't see any blue.
Jason: I'm always like -- okay. Apparently I do write TypeScript.
Orta: Apparently you do, yeah. I've heard about this thing called TypeScript. It's getting pretty big.
Jason: Who knows. Maybe this one. This one is not TypeScript. Dang it! Killing me. Who's got a library we can convert over here? Because apparently I already did all the ones that I thought were not TypeScript.
Orta: Everyone is asking for Honkify in chat.
Jason: Oh, yeah. I guess we could do Honkify.
Orta: The DTS doesn't count. We should be okay.
Jason: This one has no -- yeah, no. So let's do it. Let's do this. We have Honkify.
Orta: Open this in VS code.
Jason: Yeah, let's go over to my own.
Orta: Hue-sdk. That's probably also interesting.
Jason: Which one?
Orta: Brad mentioned he's got an SDK.
Jason: Oh, nice, yeah. So, we are going to open this up. Now, I haven't used this in a minute. So we're just going to have to hope for the best here and see what happens. We have no dependencies. That's a good sign. We've got a use honk hook.
Orta: Okay. We've got type of window. So it's definitely web browsery.
Jason: It is definitely web browsery.
Orta: We might actually find this will be impossible to type because it will be fully typed automatically out of the box.
Jason: Will it really?
Orta: Yeah. If you wrote window.navigator in there, that should just work, right? That's part of the tension of this. Just straight-up TypeScript can figure some of this contextually. Oh, no, it hasn't. So we're in a pretty good spot here. We might be able to add the dom to this. So, think about it as, you know, TypeScript has support for all of the web dom browser APIs built inside the different browsers. This is a project I maintain. It's very confusing because something Chrome might support might not be in Safari. We have to like try and figure out every time whether it should be added in there or not. So I think the first thing here is actually an interesting challenge, which is how to get -- oh, yeah. We might not have node modules set up. You probably use NPM. Doesn't matter. That's fine, either.
Jason: Yeah, either one will work. So let's just do --
Orta: Wait, it had no dependencies, right?
Jason: It created a lock tile. So would that -- I wonder if that made it --
Orta: Maybe. Probably not, though. Okay. So we just need to include it inside, at the top of this file. Okay. TypeScript has existed for eight years. In those eight years, there has been many different module loaders, right. You know, common JS is one that people are very familiar with now. Everybody is moving to import/export correctly. TypeScript's original module loader is what we'll use to import the dom.
Orta: I know. Open the triangle, and I think it's reference. Let me get the docs up for this. So what we're going to do is tell it, you know, the dom types need to be included in here. It will take the TypeScript dom types from VS code and put them in there.
Jason: Oh, nice.
Orta: VS code has them embedded.
Jason: So we have an advantage here in that I'm using VS code. VS code is a Microsoft product. TypeScript is like a Microsoft-maintained open source library. So we have the benefit of there's some amount of working in concert between tools here because you're able to kind of build these things in and work internally to make sure that people are lined up, right?
Orta: Yeah, exactly.
Jason: Now, that's not to say I'm pretty sure a lot of the things we'll see here will also work in things like Sublime or whatever your preferred thing is.
Orta: Yeah, TypeScript support in VS code -- okay, wait. I'll finish this up.
Jason: Yeah, sorry. This is the problem, right? There's so many rabbit holes. (Laughter)
Orta: So type the word reference and put a space. Then do types equals dom. So just quotes, dom, and close it up and do that.
Jason: Is it self-closing?
Orta: No, it needs to self-close. I think it's XML that needs self-closing. See how it's changed the colors?
Jason: The VS code just recognized it.
Orta: Yeah, that's something. You've got a red underline there. So I'm a little interested in what that means. The little squiggly.
Jason: Is it upper case or anything?
Orta: I doubt it. Try dom.d.ts. I don't memorize all these things. I just guess sometimes. Let's see.
Jason: Cannot find definition. Is there a quick fix for this?
Jason: Oh, that's my spell checker. Get that out of here. None of these look like -- (Laughter)
Orta: Reference. Oh, I think it's lib, not types. Lib equals dom. There's three different ones.
Jason: Oh, now it's happy.
Orta: Okay. Let's see if you get window now. Wait, what? You got a ton more information when you typed it. But then suddenly it doesn't give you anything useful in there. Oh, look at this. We're in the wrong place. Sorry, everyone. See that line, line number four?
Orta: If window is undefined.
Jason: Oh, so it's smart enough to know.
Orta: In the context of that, that window can't exist. So it has to be undefined.
Jason: Oh, my goodness. That is actually -- okay. So, that is both very cool and very confusing. It's extremely cool that it's aware of like what context we're in and doesn't try to tell us that code exists that doesn't exist.
Orta: Yeah, however, we're still not necessarily seeing it out here.
Jason: But look at this. Now we have the full everything.
Orta: That's the full dom API right there.
Jason: That is wild. This is also handy. If I'm trying to remember what something is, you know, I'm like, I want it to be query, oh, yeah, query selector all. So then as I start looking through these --
Orta: It starts to fill up.
Jason: Then I do like query and I can see query selector all. On doing that, I can see all of those. Then I hit one of these. This is super cool.
Orta: And all we had to do again was add a single -- oh, that thing on the top. The thing we're playing with here is the dom. So what we've done is tell it at the type, hey, TypeScript, we can reference the dom in this file.
Jason: So, Nicky is asking, how often is this info updated?
Orta: So, specifically the dom -- wait, here we go. You're on VS code -- I've got an extension that tells me which version of VS code is running. But if you do command, shift, P, and type TypeScript, select TypeScript version at the bottom. So you're using VS code's version of TypeScript 4.1.2, which is the latest production build. So, TypeScript itself gets an update every three months. So that's a rolling schedule. That always happens. The dom APIs are usually updated every single version. That tends to just grow.
Jason: So, at worst, you're three months out of date with your dom APIs.
Jason: And given the dom doesn't mutate that fast, you're probably in pretty good shape.
Orta: Yeah, new things are added, but no one -- not that no one is using new things, but I tell you, if you're using something that was added to the dom three months ago, to some extent TypeScript support is probably not going to be the biggest priority to you because there are ways of using bleeding edge features in TypeScript.
Jason: Right, right. Yeah.
Orta: So it's always being updated as long as the specs are being updated.
Jason: And now that's true across like everything. So does that apply to node definitions? Everything is on this rolling three-month schedule.
Orta: No, it's only the dom, actually.
Jason: Oh, okay.
Orta: You have heard of app types. DefinitelyTyped is -- yeah.
Jason: Oh, good. Beach balling. I'm at the point where Google Chrome is too much. I'm really sad about that. Definitely Typed. I guess I could have gone to the dot-org.
Orta: Used by 5.4 million projects.
Jason: That is wild. Look at that.
Orta: It's one of the most active repos on all of GitHub. 5,000-plus contributors is no joke.
Jason: Yeah, that is really no joke at all.
Orta: A change to that is automatically roughly deployed to NPM instantly. So something like, you know, the types of React or the types for node, those are bleeding edge, whatever we think is there is exactly there. But the advantage of this is also that you can get back versions. If you want to be on node 12 only, you can go and use the DefinitelyTyped things for node 12 so you're not stuck at the highest, latest build. You need to be in NPM for those.
Jason: Got it, got it.
Orta: There's like 25,000 NPM packages. We do not want to be making tags for them.
Jason: But this is actually really slick. Here's the node types. So this is published through DefinitelyTyped. Yes, here it is. Then if we were to go and look, these are locked to node.
Orta: Yeah, these tie to node versions.
Jason: That's really cool.
Orta: It's like semantic versioning is tied to the types in there basically.
Jason: That's really slick. I'm super excited that anything like this even exists because it's such a -- like, what a daunting task, right? To try to tackle just keeping everything compatible like that. What's up, Bobby Tables?
Orta: I've got a redesign of this. I haven't shipped it. I'll tell you what I have shipped, though. If you go to typescriptlang.org/dt/search, this will let you search for sort of anything across both types that are embedded inside the project because it's in TypeScript or things in DT.
Jason: That's really cool. Yeah, here's community stuff. So this is the go true or Netlify identity, which is not written in TypeScript. That's slick. I like that a lot. Look at this. This must be detected.
Orta: It's automatic, yeah. I worked with them a little on adding TypeScript support into the NPM search engine. So, the Yarn website also uses the same information.
Jason: That is really cool. Okay. You're moving to TypeScript as a team. You want to make sure that something that you're using is going to support TypeScript. You can just come here and put in the name of the package, and you'll know immediately. Does this come with types, or is this going to be an investment on your part to add the types?
Jason: That's really nice.
Orta: Yeah, some of the statistics we keep in TypeScript is the percent of random package JSONs we find off the internet that are covered entirely by DefinitelyTyped and its own types. We measure this over time to get a sense of like if I randomly download something, will it be completely typed, just by sheer luck, effectively? And generally speaking, most are now at this point. Like, nearly every major tendency is either in DefinitelyTyped or like we saw with your example.
Orta: Works for me.
Jason: So let me do this. I'll just npm init. What would be a good thing to add? I guess we can just kind of write some code. So let's start by writing some code. We'll create a source directory and an index.js.
Orta: Okay, cool.
Jason: So, in this we have --
Oh, so we would need to add code?
Jason: That's right, that's right. And thank you for the sub. I appreciate it. So, we can do like a function that we'll call --
Orta: Yeah, make a function or something.
Jason: We'll export that.
Orta: Maybe it can multiply some numbers or something. It doesn't need to be clever. It just needs to -- like, the stuff we can do from here is probably pretty interesting. Parameter one, parameter two.
Jason: Right. So then if we return num1 times num2, that's pretty --
Orta: Yeah, let's make a second file that grabs it. This is running under TypeScript now. You know, we could put a programming language in there and a parser to let you add your own expressions.
Jason: Yeah, we'll import -- it was multiply from utils.
Orta: So TypeScript is the one that provides all those dot completions and things like that, too.
Jason: That's really slick. Okay. So I've got multiply. Now it looked like -- I see this, right? That's pretty cool. We can already see now it's got the num1 any, num2 any. So let's have this console log result. Then we can run it. So we'll just node, source.
Orta: If you make them mjs files, you should be able to run that out of the box.
Jason: Oh, that's right. I will do that. Actually, wait, I think I learned something that'll make this even easier.
Orta: Oh, the type module. Yeah.
Jason: Yeah. This would be so cool if this just worked. Do it. What? Not found. Utils is not found.
Orta: It needs to be .js.
Jason: I actually prefer this now that I'm getting used to it. It still takes a little bit of adjustment. I have to remember to add type module. I have to remember my imports need the .js. But this is so nice. Like, I'm running this natively in node. I don't have to do anything magic. But check this out. We got our result. But this is not bulletproof because I can also put in here --
Orta: A string.
Jason: Potato. And you cannot do --
Orta: You can.
Orta: Yeah, you have to read the source code. First you check the website. Then you check the read me. Then you read the source code. If you're not lucky, then you go check the tests. So this is interesting. I can think of a few incremental steps to get us there, where people could say this is enough for me.
Orta: The first step is to consider what is the minimum amount of typing we would need in this file to get comprehensive typing. I think if we can tell what the parameters are, then we don't need to tell it what the return value is because TypeScript can infer that. So number times a number, it's always going to be a number. There's a rule inside TypeScript that says that's true. So to do that, if you just make a new line, just start doing a js. Press enter now.
Orta: And TypeScript takes that up and puts it into your editor. You can just hover and multiply, and it should get it.
Jason: Multiply two numbers. It says that I've got two parameters. Now, it still says whatever they want. So it's not like magic here. So if I start going in and go back to potato, you said it'll work if I just hover? Oh, yeah, it will. Look at that.
Jason: Nice, okay. I'm always too impatient. Just whipping this cursor around.
Orta: Yeah, I have that bound to a key binding. So I know where you're coming from. So we're close, right. What we have done so far is to get those two parameters to exist. Only somewhat useful. You already had that information. If you go back to utils.js, those stars, just change is to be string with lower case. Oh, not string. Number.
Jason: I was like, are we trying to break this on purpose? Who am I to question? Okay. So we'll go to number.
Orta: You will not get an error. That's also okay. But you'll be told they are numbers.
Orta: This is what I mean by migration and steps. What we've done so far is very similar to what DefinitelyTyped does. DefinitelyTyped tells you these things exist. So does the dom API. We could have gone back to that file where we had the Honkify and start looking at errors, but we didn't. Just let it exist. This is step one. Step one is letting the editor tell you this thing is right. Step two is letting it tell you when it's wrong. So on the top of this line, make a new comment. The entire file, yeah. Just to // @ts-check. There you go. There's your error.
Jason: Oh, look at that. Okay. So, a couple important things to note here. We now have TypeScript checking, but I'm not compiling to TypeScript. I can still run my code without any compile step. So this is something that I think is really, like, especially for me, the idea of having to go through and rewrite and rename all of my files and set up a TypeScript compiler and all that stuff, it just kind of becomes so overwhelming of an idea that I end up just not doing it. Well, I'll get to that eventually. I would love to have that feature, but I'm just tired. But this is really interesting because nothing changed. I didn't have to rewrite my build process. I just had to add this comment. That's super cool.
Jason: Yeah, yeah, yeah.
Jason: So this is going to correct for me. Now if I go in and try to set this to three, it's like, no, that's still not good enough. You have to make it into an actual number. Now it's happy again. And we're back to getting useful results. We get our actual result, not a number. So what happens when we do something more -- like, here's a use case I hit all the time. I'm going to export a function called validate input. I'm going to get an input object, and my input object, I want to check, let's say input.name. -- actually, let's do input.address.line 1.
Orta: Line 1, yeah.
Jason: Return false. Otherwise, we'll say this is just like, you know, very simplified. If we were doing -- and we'll name this something more useful. We'll say address. No, let's leave it at input. This would be like you have your name, your address, your email address. All that stuff. You have to validate these kind of deeply nested features. So what I have historically had to do is I end up doing this.
Orta: Yeah. Sure. I guess in this case it would be an or.
Orta: Or you can use .operator.
Jason: Yeah, we can do a lot with this and try to make it simpler. But this is what I have historically done. You don't get any help here. If I'm out here trying to validate this -- let's give this some info so when we do this -- then I'm also going to validate input, and we got that autocomplete. Thank you, TypeScript, under the hood. So then I'm going to get an is valid. That will be validate input. I have some input here, but let's say I put in, like, name Jason, and that's all the information I have. So I'm going to console.log isValid. If I run this now, it says that it's false, but now I have to go look at the source code to figure out what's valid. I would love it if it would help me more. So do I need to fully convert to TypeScript to get this?
Orta: It's an interesting question. A lot of the stuff you're doing here is RunTime behavior. TypeScript statically might not know all the information you care about.
Orta: The way in which I would type this is like having an unvalidated object and then a validated object. You sort of can switch between the two.
Orta: But talk me through a little more what you're expecting to see.
Jason: So, my thought is, like, I deal with not just a simple function that takes an input but something that takes something a little more complex. Like, the input that comes in. So maybe what we could do instead is say, like, get address. If we change this --
Orta: Oh, I see exactly what you're talking about.
Jason: We would want to input address, line 1.
Orta: Perfect. Exact same syntax as we had above, more or less. Just -- oh, you need to import it. Import is different now.
Jason: That's right, yep.
Orta: So instead of calling it a string, we just call it the type of object that it should be.
Jason: Okay. So, I'm going to add my js doc.
Orta: Then press enter. Perfect. You might know the TypeScript syntax for this because you might have done it before, but this bit here allows js doc syntax and TypeScript syntax. TypeScript syntax is significantly terser. So we're going to do that. But there is a JS doc reference that will allow people to be able to do this themselves using JS doc specific stuff. But generally, JS doc is mostly nowadays used through TypeScript.
Jason: Yeah, so what we get here, though, is I at least get a signal. I've put in this object. Now I'm going to try something. I'm going to say -- got my input. I'm going to move this out to there. Then I'm going to put in my input. It's still smart enough to know that even though this comes from a variable, it isn't the right thing.
Jason: So that's really helpful. Now, what I'm curious about is what if I do like address, line1.
Orta: Already happy.
Jason: Look at it go. So, now even though this has more fields to it, this particular input only needs to satisfy this. So this is not -- it's not like exclusive.
Orta: It's not comprehensive, yeah.
Jason: But that's actually really handy. That takes a little pressure off me. If I know all the this function needs is this, I don't have to know the whole input object shape. I just need to know what my function needs. That makes me feel a little better.
Orta: You have to write less code.
Jason: And it requires a little less context. I'm not jumping around between, okay, so I have to understand what the input looks like so that I can understand what this variable object is going to contain so that I can validate it in this function that only needs one piece of data.
Orta: Or it might be overly specific, right? That function, get address, could be useful in a user account, an admin account, shipping, separate object. Now it only cares about the underlying mechanics of how the object works, not necessarily about the sort of exact instantiation of the object itself.
Jason: Absolutely. Yeah, and, I think, is probably the -- that's just really exciting. If we want to adopt this in a way that's helpful and not just adding process for the sake of adding process, then this feels good, right? So the other thing that's interesting about this is as you said, we probably still want to have some kind of validation here because if somebody doesn't have TypeScript enabled or just choose to ignore that, they can still break this function. But what we can do here is we can adopt what you just told us that came from TypeScript. We can simplify this up a little bit. Now we've got optional chaining. So, this is going to check if line one is set. Then it'll move on because we're negating it. If any of these are not set, it won't throw an error. It'll just return -- it'll just make this condition pass.
Orta: Yeah, kind of a weird thing. Get address will either return a false or a string.
Jason: Yeah, honestly what we would probably want here is throw new error, invalid address.
Orta: But what I was going to say is that will be respected in the type system. If you hover over the result in the other file when there was still return false, you would have seen that the response would have either been a string or a false. Just line one would have been that. So TypeScript is trying to understand your code as much as possible and to try and provide -- you see that?
Jason: Oh, look at that. You see that? That's really cool.
Jason: That's really cool. Yeah, this is just so pleasant that we're able to kind of write this code. Then we get information here about what's happening. Because again, if I'm using this from a third-party package and they don't have great docs but have TypeScripts or types, then I'm able to get information about what these do, just by hovering over them. That is the kind of thing that's, like, we're not going to write docs. We all should and when we're having good days and we're not under pressure, we will. But there's always going to come that time where you just have to get something across the line, where you just need to ship something. Taking the time to write this, this is light. This will decrease the amount of errors. This is not like, okay, we'll create a new docs page, and then you have to write an explanation and all that stuff. This is code as communication.
Jason: And that, I think, is such a fascinating way to go about this. So, I think that this can be -- you know, you absolutely should still write docs when you have complex things. A utility function, it can be self-documenting. A really esoteric thing that's merging a bunch of stuff together and combining three different services, please document that. But what I like about this is it lets you focus your efforts on documenting the parts that are tricky and let the code provide the guidance for the parts that are less tricky. Things like this that are -- like, you're trying to validate something. You need to make sure a piece of an object exists for this function to not fail. I don't need you to write me a story about that. I just need you to tell me what's going to happen if I use it wrong.
Jason: So, you were talking a little bit about if you want to get pedantic with this. Another thing that I found that's really interesting is when you start getting into name types. And this can be really interesting when you've got complex input. If we were going to build this out to have our more complex address type, what if we had, I don't know -- let's do a function called --
Orta: Don't know. It would just work. (Laughter) Call it just user or something. Yeah, sure.
Jason: Or let's go city, state, zip. This is only U.S. So this is -- now we've got our -- oh, wait. Function. There we go. That's less unhappy. Then what we're going to do is return an object that has all of those things. Then we'll do our address and our line1, city, state.
Orta: City. Got your Boston accent on. (Laughter)
Jason: So, yeah, here we go. We've got this store address. The store address will be compatible with our get address, which actually means we could have a type that gets --
Orta: Passed around. This is even too easy. Like, we could just do the parameters, and it will fill everything in. The return value will be the object. Name as a string, line as a string. In fact, all of them are strings.
Jason: I think all of these are going to be.
Orta: Multiline edit that. Then the return object of that will be inferred. It's a strong object, this. And the return of store address, if you mouse over that, you don't even need to include that.
Jason: Oh, really?
Orta: Yeah, no. That will be what you want. There it is.
Jason: Oh, nice. But if I want to share this around, don't I need to name this or something?
Orta: You could. This is like where do you want to do this sort of work. So we will do that. To do that, we have to create a type definition. You create a comment that isn't attached to anything. My recommendation is open a new JS doc comment. Delete all those extras. So it is @typedef, I believe. I think it's curlies, and you give it a name.
Jason: Do you need curlies on these?
Orta: Yes. Okay, so the typedef, and inside where you wrote address, you write object.
Jason: Oh, oh, oh. I get it.
Orta: You're saying I'm going to define it. You give it a name. You can write a comment after it, which will get attached to it as well. So you use the word property to define everything that would be on that -- yeah, exactly. Everything that would be on that. If you do a new line and do @property curlies and then the type of it. So you could copy -- yeah, you could do that and just do name.
Jason: Okay. Then this one is actually going to be different. This one is going to be an object. And that's going to give us our address. Why can't I spell that today?
Orta: So this is where it gets a little complicated. How do you do the sub thing inside that? Now, I think this is a syntax. It would be -- no, no. That's not the syntax. You would make two types for this.
Jason: Oh, it won't let you do --
Orta: Well, the way I would do this is actually you see where we wrote object? If you change that to use the TypeScript type definitions, we go back to what we saw earlier. So change the word object that you have highlighted -- down one, actually. The one blow it, line 13. Turn that into curlies. Now we're just describing an object like you would in TypeScript. That would be line1: String, et cetera, et cetera. So those two syntaxes for this, you intermix them basically to get the sort of full TypeScript type system. JS doc doesn't support some of these features. So we have to support the official JS doc syntax. That's what @typedef and @property is. But then we also allow TypeScript syntax in there for the terseness. Remember, that was the first thing I said about this.
Jason: Right. So, actually, I guess this would be like user info.
Orta: If you hover over user info again, you get the object. That's the object you just described.
Jason: So here, I could do something like return.
Orta: @return, curlies. And user info.
Jason: So now --
Orta: And there you go.
Jason: It shows us it returns user info. That's great. Then here the input could be user info.
Jason: So now we've got -- it's pretty slick. Pretty slick.
Orta: You get all your autocomplete in there too, obviously. And now address isn't correct.
Jason: It's invalid because we don't have the right thing. So let's get our -- did we call it create address?
Jason: Store address. Okay. So let's store an address. I want to -- we'll make that our input. So store address, and I'm going to put in a name. This is like the worst function ever. So obviously I wouldn't write this like this, but we're trying to prove a point. We'll say Portland.
Orta: I'll answer that question from Bobby Tables down there. I described that JS doc is a subset of TypeScript support. Almost everything that you would want to do with sort of normal syntax is in JS doc. It's only when you start to get quite complicated types that you would need to use TypeScript. To some extent, you can get away with it by using the TypeScript support in JS doc like we did there for the user object. But it you're not in a complicated TypeScript, it could -- is it returns?
Jason: Oh, did we have it returned?
Orta: No, it's both, actually. Both returns and return is allowed by the JS doc language.
Jason: Oh, interesting. Okay.
Orta: The best reference is the one on the TypeScript web page that I built, so I just have that up on the side. Yeah, it would be very hard to describe a conditional type inside JS doc. You just don't have the syntax primitives to pull it off. But you could just put it in a .dts file and refer it inside your JS doc. So in theory, everything is feasible.
Jason: Can we actually look at how you would do that? This is like the next step, right?
Jason: So if step one is we do JS doc, then step two is we would have like a DTS file. So I'm going to put a new function in here. We'll make this one like, you know, add album or something like this. This is going to take an artist and a name and a year. Something like that. Or we'll say title. Then that is going to return. I don't know. We'll have it go straight up. Why not?
Orta: That's what it will be under the hood, right?
Jason: Yeah. So theoretically, this would do stuff. But what we want then is I don't want to have to write. So let's add our type somewhere else. How would I go about doing that?
Orta: Create a new .dts file anywhere you want. Could be just types.d.ts. It's just a file anywhere. Describe it like you would in TypeScript. So interface album, I think it was.
Jason: Is it like this? No t like this.
Orta: Yeah, just write it.
Jason: Interface album. And this starts to look like any schema. There's differences, but they feel similar.
Jason: And comma?
Orta: No, this is totally fine. You have it just right. Artist, title, name? I can't quite remember.
Jason: I think I said artist, title, year. Can I do something like the number has to have four digits?
Orta: You could, but that's very -- it's kind of advanced.
Jason: Is that like super weird?
Orta: It's feasible with a language feature that came out in the last version where we can sort of -- well, it could be a string that's four digits, I think. The version that's going to go in beta will allow it to be very -- like, you could say a sixth number. But let's say for simplicity's sake, you don't want to do this. Oh, exportable.
Jason: Export. Okay. So I'm exporting it.
Orta: Perfect. We're going to use one of my favorite features of TypeScript. Like, I use this everywhere. So if you go back to your utils, we need to make a JS doc thing again. We're adding some types so they have to have the JS doc thing. You could fill in your prams if you want, but the thing we care about is the returns. Let's just do @returns. Okay. We need our curlies. The curlies indicate we're going to be some sort of thing. In here we're going to write import. We can import types. So round brace, like an import statement, like a require. Then quote. You need quotes in it. Types.d.ts. If you put a dot after the end of that curly, in between those two, just one back --
Jason: Oh, I got you. So in between the round boy and the curly boy.
Orta: Uh-huh. And hopefully we should see album in there. Maybe we don't need the d.ts then.
Jason: Oh, there it is. And it's even got like the special thingy. It shows the interface album. Okay. That's slick. All right. So then I'm going to remove these. Is it smart enough to infer?
Orta: It might be. I don't think it looks backwards to that level. That's just computationally very expensive.
Jason: Fair. But it shows us that it returns an album.
Orta: So you can make all the mistakes you want, and you're totally fine.
Jason: Actually, this is interesting.
Orta: It does say anything can go there, even though you and I know it has to be a particular shape. You could very easily call this a floor with TypeScript. To some extent, it is.
Jason: So if I wanted to define my function from here, can I do that as well and apply it to this function?
Orta: Maybe, but I can't think of the syntax to do that. In the case of that, you would have utils.d.ts, and that would describe the entire shape of it.
Jason: Okay. Well, let's do that. Let's try it. So utils.d.ts. I spelled that wrong. Utils. -- come on. There. All right. So I have my utils.d.ts. So would I export types?
Orta: Well, now you're describing the entire file of utils.js. So if you can split, maybe do a vertical split -- a horizontal split with the dts and js underneath.
Jason: I can do that.
Orta: So you would need to do multiply. You would need to describe -- so it would be export function multiply.
Jason: Okay. So maybe to just save ourselves some stress here, let's split utils, and we'll instead do album.
Orta: Sure. Yeah, exactly. In part because the TypeScript resolver is prioritizing looking at the d.ts file above the js file when it's looking to see what things are in there. That's how the system works. That's how DT works, DefinitelyTyped. It'll try and figure that one out.
Jason: So I've got my album.js. They are side by side. Should I drop this one entirely?
Orta: Yeah, you could. Yeah, that would be fine. Album.d.ts, you would have export function. Now you're basically writing TypeScript. You could do your curlies.
Jason: Like this?
Orta: Yeah, exactly. Then here it would be like album?
Orta: Yeah, now, you don't have album.
Jason: Well, we can move that over.
Orta: It was offering to auto import it.
Jason: Oh, was it really?
Jason: That's fascinating. Wait, get out of here. Go away.
Orta: Or maybe not. Yeah.
Orta: This is one of my favorite features of VS code recently. If it auto imports above, it will not jump your code down. It will actually shift your window up at the same time as importing it so you stay in the exact same context.
Jason: That's slick. Okay.
Orta: So that's taken the type.
Jason: Okay. So if I look at this now,
Jason: Add album from -- what was it? Album.js?
Jason: Okay. Look at it go. Now it's yelling at us, which is what we want. So yeah, we could do -- of course, I'm going to draw a blank. An artist and an album. Like, I'm done.
Orta: Jimmy Eat World, Futures.
Jason: There we go.
Orta: Yes, Jonathan, there is a bit of a difference between export function and declare function. Declare function, generally speaking, declares something globally. Export function in this case is saying this is an exportable function here.
Jason: I just made up a year. Maybe?
Orta: I would say more like 2004.
Jason: Was it? I don't know. Fact check us, chat. How did we do? Whoever is closest without going over.
Orta: Love it.
Jason: Yeah, so this now has everything that we would expect. It's telling us if we're right or wrong. If I try to make this a string, it's going to yell at me. That's all happening without any import. We just have the ts-check. Even if I remove that ts-check, it's still pulling in that information. That's so cool.
Orta: So, one of the trade-offs we've just made is the lack of co-location. The same advantage we used to have, the docs were right next to the code. It's less likely to go outside of it. Now it's a separate file.
Jason: By the way, chat verified you were exactly right with the year of this album.
Orta: Jimmy Eat World are doing a bunch of live shows pretty soon. I'm really excited to see them. Well, not see them. Not live.
Jason: Yeah, yeah, yeah. No, that's really awesome. Okay. So Jonathan has a good question. Is there a difference between export function and declare function?
Jason: I cut you off when you started talking about trade-offs, but this is actually something interesting to discuss. We did give up this clarity here. If I go inspect the source, I don't get that insight anymore. So the benefit is that we can be very TypeScript specific. So the next evolution of this, if we wanted to go full-blown TypeScript, would be that we would write this as a .ts file and write our function in here, is that correct?
Jason: Sure. So, one more question. We've got about 15 minutes left. If I want to turn just this album.js into a TypeScript file, can we do it inside of 15 minutes?
Orta: Trivially, yeah. We could probably do it in 60-odd seconds.
Jason: Let's do it. Let's learn how to make this happen. If I want to compile TypeScript -- and to do that, what I'm expecting will happen, I'm going to copy this over to here.
Jason: Now, I expect this to be my TypeScript. So that means that I'm going to rename this one.
Orta: Yeah, I'd delete that album.d.ts, too.
Jason: Okay. So we'll delete that. Okay. Now I have a TypeScript file. I don't have -- I'm just going to pull this over, I think.
Orta: It could reference it if you wanted. There's some nice specific syntax for this that's like import type that's separate from importing modules.
Jason: Oh, interesting. So just import --
Orta: Write import space type, and then your curlies because it's a curly import.
Jason: So I've got that. We're ready to make this into TypeScript. If I try to run this right now, it's going to blow up. What am I doing here?
Orta: You've got a trailing end.
Jason: Get out of there. All right. So this then -- cannot find module album.js. So if I switch this over to be album.ts, that's immediately going to explode.
Jason: So I don't want to do that. Instead, we're going to use TypeScript to compile TS to JS?
Orta: Yeah, realistically, there's sort of two ways to do it effectively at this point. There's either use something like TS node, which instead of writing node, you'd write ts-node, and it would just try and figure it out. The other is to run the TypeScript compiler.
Jason: Let's generate. That feels like more what we would deploy. So what do I --
Orta: I don't know the npm install. I've been using es build in some of my personal projects. So TypeScript is a single dependency, which is quite unique inside the ecosystem. It's just one massive single dependency.
Orta: And it adds a command tsc, which I think you might have to add to scripts, otherwise you have to use npx.
Jason: Then is it just like source?
Orta: Well, yeah, you might.
Jason: Because we want to put it somewhere, right?
Orta: TypeScript uses this package equivalent. My gut says if you run it like this and just run npm build, it might just work. With the default settings.
Jason: What does it not like? It says --
Orta: Oh, I bet it's not happy because there's no project. Yeah, okay. Well, do npm run -- npm build -- init. Just like when you did npm init. Same thing.
Jason: So it'll use the local one if you do it like this, which is nice. Successfully created a TS config file. We had a target of es5, a module of commonjs.
Orta: Which is not necessarily accurate. For our system, we're using imports correctly. You could call it es2020.
Orta: And in theory, you could do the same for the above. So TypeScript will try and reduce it to the oldest possible browser, but you're running in the most modern node possible. It should accept both. It's pretty flexible on that. Some people like to write dom in lower case. Other people like to write it in upper case.
Jason: Sure, sure. So then I want an out directory of here.
Jason: Okay, okay. So that then should do it?
Orta: Oh, strict true might be a bit excessive in this case.
Jason: Okay, let's turn it off.
Orta: That should be everything.
Jason: All right. Let's try it. I'm going to npm run build. It runs TypeScript. And we get album.js, which looks exactly like our original album.js. However, now we still get all of our information, right? We get everything we expected. Yeah, sorry. We get everything we expected. We've got our what's coming in, what's coming out, and if we go over to the actual code that we're writing with album.ts, we get all of that information here as well. And that, I think, is really, really slick in the sense that it tells us what's going on, which I really appreciate.
Orta: In this case, you only have one source of truth now, right?
Jason: Yeah, which is -- and as you said, you know, it's a little tricky if you're looking at it like we've got our album.js. This has no information. So if I have to look at this and then I have to open a .d.ts, I can see how -- you know, you have to discuss the trade-offs. The speed of getting up and running with utils like this, this is incredible. The speed with which we were able to just quickly say we want type checking, we want autocomplete, add this js doc. Like, 100% this is where I'm going to start.
Orta: Yeah, for sure.
Jason: And then it kind of seems like as you get deeper into this, as things get more complex, as you find yourself passing types around, then it makes more sense to start looking into like a deeper integration. If it's really, really everything is going to be typed, you're starting green, maybe it makes sense to go from fully compiled TypeScript from the get go.
Jason: Sure. But yeah, and this is really slick. I'm a big fan of the incremental approach here. I'm a big fan of this not being like, we're switching to TypeScript, everybody drop everything and let's rebuild the whole app. It's like, what are the most critical pieces of our app? What are the things that are most shared and least documented? Can we convert those to TypeScript so we at least get some hints? And stop tripping people up. That to me seems really, really valuable.
Orta: Yeah, you find the small bits of code on the edges and move them in and slowly get to a point where you've got good enough tooling.
Jason: Yeah. I love that. I think that's really, really exciting. And so, all right, we got a couple minutes left. Chat, if you have questions, pop them in now. So Nicky, I'm not gown to run it with Dino because I don't have it set up.
Jason: A couple things in here. Token 808 says, you know, my issue is that it feels a bit like a draw the rest of the owl problem, which means it starts out easy and gets really hard. We're trying to combine TypeScript with React. I think that's fair, but I also -- here's my suspicion. My suspicion is this is not an issue of any particular tool but an issue of lots of tools that weren't built to be aware of each other, all trying to be combined together. If you greenfield a React project and build it with type script and you're not importing third-party tools not build with TypeScript, it's all great. Everything is wonderful. But then you kind of import a tool or a package, and that package has a third-party type definition, and it's only 65% complete. Then you're like trying to do something else and suddenly you're getting these weird errors and you have no idea where to even look for the issue. That's a real thing. And that's maybe one of the trade-offs to consider when looking at something like this.
Orta: It's a little bit like dealing with a web pack config. Sometimes you have to really do the research, go really deep to find your eventual answer, and it's like a one-liner somewhere that you'd never expect it.
Jason: But I think there's a good argument to be made here. It doesn't have to be one or the other. You don't have to go all in on TypeScript. You can go in on TypeScript for internal tooling. Don't fight it when it's not compatible with your project. If you're working on a React code base that's largely built with third-party modules that weren't written in TypeScript, maybe that's not the right thing. But your internal tools, your knew utilities, the components you're writing now shared between your UI pieces, write those with TypeScript definitions and maybe use JS doc so you get the auto complete, the warnings. Then you can add that ts-check at the top and be made aware. Oh, we're trying to add the wrong props to this component. Perfect. How great is that?
Orta: Just a little bit of documentation here and there makes your life a little better.
Jason: Yeah, absolutely.
Jason: Yeah. Here's a question. Looking to the future, what do you think is next after TypeScript? Like, what are you paying attention to right now?
Orta: I built a games engine for Snowpack. I press save and it hot reloads, the game engine. That would be infeasible in Webpack to me.
Jason: And that actually points to something interesting, which is that it does feel like we had tools like Webpack to pave the way to get us to here. Now on the backs of that work, through the efforts of the Webpack team, we've opened the door to a next generation of tooling that's going to take that work and go even further. And I am really, really excited to see things like Snowpack all coming out because -- and like there's really cool stuff coming in from the Rust community as well that is starting to look at how can we make this faster, how can we make this more -- like, let the browser do what the browser does. Don't fight against it so much in the name of uniformity. Really, really interesting stuff. Unfortunately, we are out of time. So, if somebody wants to learn more, where should they go next? Let me -- I'm going to throw the TypeScript docs back in the chat. And I'm going to make sure to point to your Twitter again. Oh, come on. Don't beach ball me now. It's not the time.
Jason: Is it for JS programmers?
Orta: Instead of trying to do it as a binary move.
Jason: Cool. Well, thank you all so much for hanging out with us today. Orta, thank you so much for giving us your time and walking us through this. I think this was absolutely fascinating. Make sure you go and check out the schedule. We've got really good stuff coming up. This is going to be a really, really fun -- like, this year is starting out so good. On Thursday, Emma is coming back. We're going to mercilessly ridicule her for her taste in tacos. Then Ali Spittel is coming on. We're going to learn React. Then Scott Moss with JS. That's going to be so, so fun. Ben Hong is coming back. Sarah Drasner is coming on. That is going to be interesting. We're doing more of an AMA and not a code along. But it's for something really interesting and really important. There are a lot of people trying to get a job right now. The industry is super competitive because there's so many people coming in. We're going to talk about how to stand out, how to get that first job. We've got WordPress and Next. It's going to be so much fun, y'all. I cannot wait. So, make sure you check out the schedule. Thanks again to Rachel and white coat captioning, who have been providing live captioning for this entire show. We really, really appreciate that. That is made possible through the generous sponsorship of -- let me actually show the logos. Why not, right? Through the generous sponsorship of Netlify, Fauna, Auth0, and Hasura, all of whom make this show more accessible to more people. Orta, thank you again for hanging out today. Chat, stay tuned. We're going to raid. We'll see you next time.
Closed captioning and more are made possible by our sponsors: