Migrate Open Source TypeScript Libraries with Tanner Linsley

Joe chats to Tanner Linsley about the technique and practice of migrating large open-source projects to TypeScript.

Show Notes

Tanner is one of three co-founders at Nozzle, where he leads front-end development on their SEO rank tracking and monitoring software. He’s been with the company since its creation in 2014, but before Nozzle, he got his start in open source, working on WooCommerce and WordPress sites. When he found Angular, he found his first connection to JavaScript.

Tanner has since become a well-known figure in the open-source world. He’s created many open-source libraries under the umbrella of the TanStack; he and Joe talk a bit about two of them—React Query and React Table—during their conversation. It was feedback on his open-source libraries that initially prompted Tanner to start learning TypeScript - his contributors and users kept requesting it.

Once Tanner started seeing the benefits TypeScript brought to his open-source libraries, he knew he needed to start using it at Nozzle too. That was about two years ago, and he has been focused on migrating Nozzle’s existing codebase from JavaScript to TypeScript since then. The process is still ongoing and has involved a lot of learning on his part. He explains some of his stumbling points and successes along the way.

He also dives deep into generics, a piece of TypeScript that he’s been focusing on a lot recently. He walks through the use cases he’s had for generics and how he’s using them now in his current libraries.


[00:00:00] Joe Previte: Tanner, what's up?

[00:00:02] Tanner Linsley: What's going on man? Maybe let's start just

[00:00:05] Joe Previte: With your background. When you started getting more involved in open source was type script even on your mind? Nope. ,

[00:00:15] Tanner Linsley: I didn't even know what type script was. Like, I got an open source when I was still doing WordPress and I didn't even know what.

[00:00:25] Tanner Linsley: I didn't even really know what JavaScript was. Like I knew what it was, but I didn't even know what it would be like today. Nobody knew, but , I was just doing WordPress sites for people and I was doing like WooCommerce stuff, trying to just make some extra money. And yeah. W Commerce had a really thriving community around it, like with open source.

[00:00:48] Tanner Linsley: So I was like, Oh, this open source thing's pretty. That's my first entry into open source. And then it was finding Angular and like I found Angular before I even knew I wanted to be into like front end development or [00:01:00] JavaScript, I found Angular cause I was like, Dude, I can build iOS apps on this thing called Ionic.

[00:01:05] Tanner Linsley: And they uses Angular, which uses JavaScript. And I'm like, Oh, I know JavaScript. , like that was, those are my two entry points into open source. After that, everything just came naturally with time. And so how would

[00:01:19] Joe Previte: you describe the relationship between nozzle and open source?

[00:01:23] Joe Previte: Were you doing open source before you started

[00:01:25] Tanner Linsley: working on nozzle? Yeah I was working as. First I was just an engineer at this scrapbooking, digital scrapbooking company. And they had magazines too. And then one day the VP of Tech, they're just like up and left. They're like, Hey, you're promoted.

[00:01:46] Tanner Linsley: And I was like, Oh crap, . I don't know what I'm doing. I was like I had only been like actually programming for a year, and I just had no idea. Like [00:02:00] literally weeks prior to this, I had almost accidentally RM dash r the entire like universe, like that's how novice I was and.

[00:02:11] Tanner Linsley: Then they're like, Hey, you're promoted. Cuz nobody else knows how to run any of this, so you're the man. I was like, Okay, whatever. And so that's how I got into it. I started I started building stuff in WordPress and then, I, did this whole digital magazine thing going on with trying to hack WordPress into stuff.

[00:02:32] Tanner Linsley: And before I knew it, I was just like, that company was just tanking. I was like, the owners were just terrible. But I came to work one day and the doors were closed, and my, the other guy that I worked with there, the, me and him ran the show a little bit and he was like There's no more money left.

[00:02:46] Tanner Linsley: The owners took it all and it's just me and you, so it's time to close up Shop . Oh my gosh. And so ironically enough, I had been attending I had been attending meetups here in Utah [00:03:00] at a company called At Task. This was before they were called Workfront and they were angular meets. And I just, cuz I was learning Angular, I just loved.

[00:03:09] Tanner Linsley: And I was building like a little app on the side without you on it. And I had met my now co-founder at these meetups and he was just there looking for front end devs and just exploring. And I called him up and I was like, Hey, remember how you were gonna start a company? When are you gonna do that?

[00:03:29] Tanner Linsley: And he's Oh I don't think we're gonna do that for another few months. We haven't secured the funding yet. We're planning on it. Why? I was like my company's dead. The company I work at, I'm like, I need a job right now. I don't have two weeks. I don't have severance.

[00:03:43] Tanner Linsley: Like I need a new job asap. He's Oh crap. Don't go get a new job. I, we, let's here's what we're gonna do. So he was the VP of Tech SEO dot. And okay, he's I can get you a job@seo.com.[00:04:00] He's Come and interview. And I was like, Okay. So I walked in, it was just him. He's Hi so you want a job

[00:04:06] Tanner Linsley: I'm like, Yeah. And he's Okay, you're hired. . Oh, Yeah's, just the easiest interview. So I was like, Okay. So I worked for seo.com for a month or two. And the o like the. Let's say the president of seo.com, he knew the arrangement too. He's Yeah, this guy's gonna be gone in a few months when they start the company, but whatever, We'll take what we can get.

[00:04:28] Tanner Linsley: So I worked there for two months and then one day, my, my two co-founders, they came in and today's the day I was like, Just, let's do it. So I was technically the first employee of Nozzle. . I was not a co-founder. I was just an. and within the first two or three months, I guess I impressed my co-founders enough that they decided to deal me in whether I liked it or not.

[00:04:54] Tanner Linsley: I just showed up one day and they're like, Hey, so we have awarded you this many shares. You're now a [00:05:00] co-founder. Welcome to the Club . I was like, All right. I didn't really ask for that, but I'll take it. That's amazing. So that's how, that's kind how Nozzle started. That was eight, almost nine years ago.

[00:05:14] Tanner Linsley: Okay. We subleased an office from massio.com, and started nozzle. And it's been a whirlwind since nozzles. It's it's been around for a while. We've been working really hard on building something different, like really fresh and new. And we're about there. Like we have some amazing tech.

[00:05:32] Tanner Linsley: Right. and just starting our little hockey stick . Yeah. Let's,

[00:05:37] Joe Previte: let's dig into that then. Since we're talking about nozzle. Obviously you're unique in that you have the library author experience working with Cript and then also the co-founder working at a small startup working with type scripts.

[00:05:49] Joe Previte: And I know you said that you've. Migrating two type script for every year. Talk to us about that. How did you decide to bring type script into the stack and how have you approached [00:06:00] migrating to it?

[00:06:01] Tanner Linsley: Type script came into my world almost by force. Like we weren't using type script at Nozzle cuz I didn't know type script , so I decided we weren't gonna use it.

[00:06:16] Tanner Linsley: I knew about its benefits. I knew about strict types compiled languages. Like I was familiar with them from, other parts of the programming and engineering space. But in the front end world, I was like, type script is just like this weird thing that I don't really understand a whole lot about, so I'm not gonna touch it right now.

[00:06:34] Tanner Linsley: I had this feeling that I needed to learn it eventually, and I was just waiting for all the pieces to come. So where I was like, Okay, I absolutely need to learn this now. Now is the right time. Cause I knew there was gonna be some downtime with productivity while I learned this new muscle memory thing.

[00:06:51] Tanner Linsley: And really it happened slowly started to creep in on me from open source. Like I had a probably a good year or two where like [00:07:00] I was battling back against people on Twitter who were like you, you need to rewrite your library in type script. and I'm like, How dare you ask me to do that.

[00:07:08] Tanner Linsley: That is a huge ask. One I don't even know type script, so you can't ask me to do that. And if you want types so bad, these types , go make 'em yourself. I don't care about those types. And you don't know what you don't know, right? And in this case, like I knew types would be great, but I just knew that I couldn't achieve them the way that they wanted me to.

[00:07:30] Tanner Linsley: So I was just like, don't be so entitle. Wait for me to learn type scripts and someday you'll get 'em. Eventually, I I was working, so I was working on React Query version two, I think. Okay. Version two, or ver it was version three and I was like, Oh, it'd be really great if the types were just built in

[00:07:49] Tanner Linsley: Like it just one day I was like, ah, we should do type script. And I found this guy, he's contributor. Haven't heard from him in a while. He's probably busy doing more important [00:08:00] things, but he just popped in outta nowhere for a month or two and was just like, Hey, I'll help you rewrite react query with type scripts.

[00:08:08] Tanner Linsley: I was like, Okay. Can I see how you do it? Teach me how, So I over the course of many prs, I watched him rewrite React Query to this type script library and saw some of the ways that he did that and how it changed the API a little bit. And that was the biggest starting point for me of learning type scripts.

[00:08:29] Tanner Linsley: Seeing someone take something that I had created and turn it into type script was super. Like empowering for me. I was like, Oh look, it's my code, but it's all ified, so after he did that he kinda just stepped down after that. I think he got really busy. But since then, like a lot of the core types that you see in type script were actually contributed by him.

[00:08:53] Tanner Linsley: And he's just, he was really great with type. . Also a big fan of classes, so you'll notice that if you look into the [00:09:00] React Query source code. But that was the first moment and after that it was then the expectation, React Query is a type script library now, and I'm like this guy's not gonna be around forever, so I gotta know what the heck's going on.

[00:09:11] Tanner Linsley: So I just started really digging in at that point, reverse engineering these types that he had built. Generics just blew my mind like, I feel like when you're learning TypeScript it's just and that was a different experience too for me. That's where I feel like my learning experience with TypeScript diverged from many others.

[00:09:30] Tanner Linsley: Cause I feel like a lot of people will get into it and they'll start building an app and they're like more of a consumer of generics in a way where you're like, okay. This library or these utilities are giving me the types. And ideally you don't really see a lot of like type annotations in your own code other than some function signatures here and there.

[00:09:51] Tanner Linsley: . But as a library author, like as soon as you build a library that really does [00:10:00] anything remotely useful, you've got generics to worry about. And so I, I just got thrown into the deep end. With building library types and I tell people all the time, they're so different. You can ask you can ask Ace Mark Erickson.

[00:10:14] Tanner Linsley: He maintains redux and does so much with he will echo this with me to the ends of the earth. Like library types are so different. So it took me a lot longer to learn. Type script. I feel like, because I just, I had to know these advanced concepts outta the gate, so that was a big differentiator for me, like in my learning experience.

[00:10:38] Tanner Linsley: Some people are like, Wow. Like type script's? Not that hard. Have you ever written, have you ever written a library before?

[00:10:46] Joe Previte: Oh man. That just sounds very intimidating. One thing though when that contributor came and said, Hey, let's migrate this to type script, did you have any hesitations with that limitating?

[00:10:57] Joe Previte: The circle of people that can contribute cuz [00:11:00] now can

[00:11:00] Tanner Linsley: have type. Okay. I did, and that was one of my pushbacks in the beginning. There were discussions very early on about should we do type script? And I was like, No, because then anybody can, anybody that comes in needs to know type script and there's not a lot of people to know type script.

[00:11:14] Tanner Linsley: And it took me a long time to come around to that. It's there, there are trade offs, right? But I feel like as long as you have the primitives, in the library, the type stone really well. Cuz types are just like any other, like JavaScript code or programming code. I feel like if you write the abstractions well then they should be easy to work with.

[00:11:39] Tanner Linsley: And that's like working inside of a library itself is a completely new level of developer experience and I want that to be just as good. For me, like when I go in and maintain these libraries, I want it to be just as good as like using the library. . So for me it was seeing that was possible to get to the point where, you know, okay, even if you don't [00:12:00] know type script, you can make meaningful contributions to this library.

[00:12:04] Tanner Linsley: And hopefully maybe it, it'll be a good learning experience for someone who's doing that. It took me a while to come around to that. And I believe his name was Nick. He was the one who helped me get to that point. And I feel like also there's been this shift in the ecosystem where, it, it takes a lot of like mind share and a majority of the ecosystem to say, tip the scale.

[00:12:31] Tanner Linsley: Okay. Yes. Type script is important. Now even if you don't use type script, like the auto completion is important for you and I think it's okay for me now to say if you want to contribute to open source types are now an expectation of open source libraries. And so if you want to get your hands dirty and open source with JavaScript, you're probably gonna need a no type script.

[00:12:54] Tanner Linsley: I wouldn't have said that two or three years ago, but today I am . [00:13:00]

[00:13:01] Joe Previte: When do you think that tipping point was

[00:13:03] Tanner Linsley: over the last two or three years? For me, I'm sure it's different for everybody. You know it like if you were, if you had a library that was really big four years ago, you might have already had type script in there.

[00:13:17] Tanner Linsley: If you have a library today that's brand new, not that many users, and you just hacked it together in JavaScript, you probably don't think types are important yet. I think there's laws of scale there. If you plan on something getting big or being widely used and if you want it to be prolific, then you need, probably need to have types in there eventually.

[00:13:35] Tanner Linsley: So the good thing that you need to realize though, is that at the end of the. It all gets compiled out. The types are just gonna help, They're gonna help the types grouped users, but they're also going to help regular users as well, even if they don't feel the types directly. , like they're going to get those benefits, right.

[00:13:54] Joe Previte: Yeah, that makes sense. Going back to the contributor question, so when you did finally migrate two type script did you [00:14:00] notice a decrease in contributors or an increase, or did it stay the same?

[00:14:04] Tanner Linsley: It I feel like it kinda stayed the same, but it changed like the, I feel like the quality of contributions went up because the, the bottom line for contributing changed.

[00:14:15] Tanner Linsley: You need to know type script now, and for better or worse, knowing type script usually helps you be a better developer. I would say 99.9% of the time. Types in general, Knowing this, the static types of your language will help you be a better developer. You think better you have more mental capacity to think about the problem rather than holding types in your head.

[00:14:42] Tanner Linsley: Like type script will make you a better developer, no question. So I feel having that be a barrier to entry was probably a good thing. I got fewer prs, but they're really good quality and. I think also knowing that it was type script attracted a new type of [00:15:00] contributor, a new type of maintainer that maybe wouldn't have been interested in it before.

[00:15:06] Tanner Linsley: So it changed, but for the better. Having more contributors is not necessarily a good thing. If those contributions are me , more quality. Yeah. Yeah. Yeah. And yeah, so I guess going back to your kinda the original question Typescripts snuck in on me through open source as like a, like an impending requirement for me to con continue my progress through in my open source career.

[00:15:33] Tanner Linsley: I had to adopt it and then, After I had gotten comfortable with it in React Query and a couple of other libraries, I was like, I can't live without this in Nozzle. Like we gotta get types into Nozzle asap. And I still say that today, like it's not fully migrated yet, but like every time I go into the nozzle code base I'm just like, Oh, like I wish this was just [00:16:00] 100% migrated and everything is good and we're getting there.

[00:16:04] Joe Previte: Yeah. So when you decided that moment to bring it into nozzle, did you have to convince other teammates?

[00:16:11] Tanner Linsley: Nope, because at the time I was the only teammate on the front end. So Nozzle is about 13, 14 people and we have, a handful of backend devs. Our CEO. And founder, he's like a database backend, goling wizard guy, very talented backend engineers all throughout nozzle.

[00:16:35] Tanner Linsley: But when it came to the front end up until about a year and a half ago, it was just me pretending that I was this big team. fake it until you make it right and the startup, you gotta be lean. And up until that point I was able to. Everything that we needed on our own.

[00:16:52] Tanner Linsley: On my own, but the laws of scale started to bite me, so I needed to hire. And luckily, my first hire [00:17:00] Jake, he came in at a time where, we were about to migrate to type script. So it was easy for me to say, Hey, everything you're gonna see is in JavaScript. I'm so sorry.

[00:17:12] Tanner Linsley: And he's that's okay. Cause I don't know type script, and I was like, Okay, we are a type script shop now even if our code does not say it. So we're slowly moving to type script. So he's been able to learn and grow with type script at his side, which is I'm super jealous of, so it was an easy decision for me to just say, no types are coming in. And all my backend guys were like, Yeah, it's about time. Cause we're over here in Go Lang. Everything over here is statically typed for good reason. And I'm like, Yeah, I know. I get it. I know the benefits. I'm just saying the time is right.

[00:17:46] Tanner Linsley: So we're gonna do it. Yeah. Oh, there's still so much to do though, .

[00:17:50] Joe Previte: Yeah. Talk, Let's talk about that. How do you even, nozzle at this point, I guess has been around seven years maybe when you made that decision, or eight years. How do you [00:18:00] even approach migrating to type script or bringing type script into the front end?

[00:18:04] Tanner Linsley: While I think about that question, I'm just going to. Go and do a c lock on nozzle really quick. . Okay. Cause I want to know the code mar the code makeup, it doesn't include all of nozzle, but the, Okay. The main, like the main project directory in our monorepo for most of the front end code today sits at around 60 to 70,000 lines of.

[00:18:35] Tanner Linsley: Oh my gosh. And only 2000 of that is comment or no, let's see. That's actual code lines. Okay. So that's a lot that, that's a lot of code. Yeah, and it's been more, obviously when we moved to React Query, we got rid of like thousands of lines of code . But we knew that we couldn't do all of.

[00:18:58] Tanner Linsley: Just in one big go. [00:19:00] And because of our small team and like our velocity right now and how quickly we need to be shipping features, like saying, Hey, we're gonna rewrite. This was just not an option. Like maybe if you have a big company and you say, Yeah, you know what, we're gonna gather the type script Avenger team, and.

[00:19:19] Tanner Linsley: They're gonna start writing this new version of the app and or maybe they do specific, sub trees of the app in type script. Like it's just me and my one employee. We don't have time to do that. We're both shipping new features all the time. So it had to be a gradual thing. And at the beginning it's just okay, how do we turn all these job script files into type script files?

[00:19:44] Tanner Linsley: And. I was just like, I don't even wanna mess around with the extension. So I just turned everything into a ts a TSX file. And then I went through on any components that didn't need jsx, I just turned into ts. [00:20:00] So then I just hit allow JS basically, . So it's like turning on the type script compiler and then just turning off all the benefits of TypeScript.

[00:20:06] Tanner Linsley: It sucked really bad. I. I didn't want to do that, but it was really the only way to get to get things moving. And eventually we started turning on some of the rules, right? So the first step was just getting everything over to type script and replacing our build toolkit, which at the time was using next JS to.

[00:20:29] Tanner Linsley: Just to use types and to get our Ts config and everything. And it's a lot of work just to get type script, just set up, make sure that everything's working right. Like our ts config right now has trans pile only, like it's not actually stopping the build. If there's errors, obviously, cuz everything's, there's red everywhere still.

[00:20:52] Tanner Linsley: We allow implicit any we allow, There are not strict nu checks right now, , but other than that, everything else [00:21:00] is set to strict. And this is obviously over time we've been able to do this. We've been turning on like little things here and there, but right now, like we still have a lot of red lines and nozzle for types.

[00:21:11] Tanner Linsley: Really, I just, I can't wait for the day when I can turn on strict nu checks and turn on. No implicit any, like those two things. So many bugs come from those things. But if I were to turn those on today, like the entire app would be red. Because there's just so many implicit es and all the array accessing and property accessing was strict.

[00:21:35] Tanner Linsley: And Nu checks is just everywhere. Anywhere you're doing like an array dot find array, dot filter, like array dot anything seems like you got strict nu checks biting in the butt. That's the next step and unfortunately I haven't found a good way to turn on those rules for specific files.

[00:21:54] Tanner Linsley: Like you have to do it with a directory structure in a way. I'm not sure how to approach that yet, but [00:22:00] that's gonna be the next big step for us is saying, okay let's get rid of let's turn on probably strict Nu Checks first will be the first one to come on. . And then after that it will be no implicit any, and that's gonna open up a whole new.

[00:22:13] Tanner Linsley: Like type checking arena for us. And part of that, and we can get into it if you want, but part of that gets into the types generated from like our schema and backend systems come through. Like I think it's highly underestimated how much of your system is reliant on those types. Yeah. And right now I don't, we don't have a good way of doing that. Like we. All of our APIs built, like with Pro Buff and Goling and there's like many layers, it's a first class api. Our API is also a product, okay. But like they are still working on getting swagger, like JS doc type stuff out, Not JS doc, What is it called?

[00:22:58] Tanner Linsley: Jason? [00:23:00] Jason api.

[00:23:01] Joe Previte: Something like that. Or

[00:23:02] Tanner Linsley: no swagger? I'm not sure. The other one, I think JSON API is like what? Swagger V2 or something like that. Okay. Like they're working on getting that going so that I can have those types. But for today, like honestly, I feel like every day I'm finding a new response from our API and I'll go into the network tab.

[00:23:21] Tanner Linsley: Copy the actual response from the network tab and run it through one of these tools. Like I always just Google it. I don't even have it . Yeah, but it's like JSON to type script type or something. Something. Yeah. And it's oh, make types from JSON samples. And I just paste in this JSON and it creates all these types.

[00:23:38] Tanner Linsley: And then I just take those and shove them into my Types file where I'm just basically lying to my app about the types I'm getting from the api. And I have this, I have this this proxy layer kind of API file where I'm like, Okay, I'm using Axios or whatever to get all the things.

[00:23:54] Tanner Linsley: And that's where I strict type it. . So then everything beyond that, like [00:24:00] whether it's React query, Whatever's consuming that, like that primitive like promise it has the right types. So it's wow, it's messy. Yeah. Migrating to type script. Seriously. And you know what's funny is I think if you were to rewrite an app, you're like, Oh, we're gonna rewrite this in type script.

[00:24:19] Tanner Linsley: That's the very first question you should ask yourself. How are we going to generate the types from wherever our database. all the way down to a client and there's a thousand ways to do that. Yeah. Honestly, and. That brings up questions of okay, how are we gonna ensure type safety from the back end to the front end?

[00:24:43] Tanner Linsley: Can you do that? There's gotta be, there, there has to be runtime checks involved. Are we gonna use z? Are we gonna use, Oh yeah, whatever. There's tools out there that are really cool, like T rrp C. But that assumes that you're using Node on the back end too Ah, okay. Yeah, [00:25:00] I feel like there's a lot, and then there's the whole GraphQL camp where it's like, Whoa, we got GraphQL so we can generate all the types, for everybody all the time.

[00:25:07] Tanner Linsley: And it's okay does that mean that everybody using your API has to be using GraphQL or do you have a rest client to, There's so many facets to. I don't even want to think about those right now. It seems just like every day I just go to like my api, like my going API guy who manages the API for me, and I'm like, Hey, I need the types. Give me all the types. He's We're working on it. We're working on it. , he's probably tired of you bugging him. . I know that's gonna be the next big step though, is just getting those types. Yeah, because after that I, I feel. People sometimes think Oh, they're moving to type scripts is big thing.

[00:25:46] Tanner Linsley: If you can type your primitive objects and functions really well out of the gate the best case scenario is that most of your code just doesn't really have a whole lot of type round notation. , the types come [00:26:00] from the return types of your functions and generics. Hopefully you don't have too many of them in your application code.

[00:26:07] Tanner Linsley: . Are really simple. Something that's like T data or TRO or something like that, yeah,

[00:26:12] Joe Previte: man, that, that does sound super complicated and yeah, especially like when you have a team size that small, like how do you balance the new feature work with the type script migration, which, should catch bugs,

[00:26:24] Tanner Linsley: for example.

[00:26:26] Tanner Linsley: Yeah. Most of the time it's just Hey, Jake, if you see. Anything that can be typed better, just do it, , and he's Okay. And as we build new features, all the new stuff we're building with, the best types that we can get right there. It's not, still not gonna catch some stuff though, because we don't have all the strict stuff turned on.

[00:26:44] Tanner Linsley: And that's the bummer is it's I would rather be able to turn on strict nu checks. And all of the strict stuff for all of nozzle. And anywhere that there's an error, just have a code, I don't know, have a code shift [00:27:00] script that would go through and just mark all of them as any put in as any or colon, any, everywhere.

[00:27:06] Tanner Linsley: Like just make it so that I can have strict mode on. But my app compiles like, No implicit any, Yes, don't turn that on. But we're, but anywhere that there, that's gonna error. Go in and put like colon any or as in, or whatever. So that any new features I build and any new code that I write can be 100% strict type script. And then it's a matter of going back through as you can and removing the anys that are in your code. You go back to, you're gonna go rewrite a feature or upgrade a feature, you say, Oh, as any, that's dumb. So you get rid of the as any, and you handle it there, but then at least you have a clean slate going forward where it's like, Yeah, oh look, we wrote this new feature, but the app isn't compiling because the new code that we wrote isn't passing, types type checking.

[00:27:58] Tanner Linsley: , that's an idea that I've [00:28:00] had that I would like to do, but I don't know how to do that in a good way right now. There was a let's see, I gotta remember his name really quick. Okay. Joshua k Goldberg. Yes. Yeah. Do you know Josh? Great guy. Yeah. Okay. Josh Goldberg is a saint. And he has been trying to help us with this problem.

[00:28:26] Tanner Linsley: So Josh Goldberg, let me look his up, let me look up his Twitter handle. Okay. He is he was at Code Academy for a while. He was at Microsoft. Loves accessibility. Yeah, he just loves type script, but he's very talented and he's been writing this this tool for migrating from JavaScript to type. and his first, I think his first like audience was like, How do we take a JS app and move it to type script, right?

[00:28:56] Tanner Linsley: But he's Oh, I wonder if we could run it on like an existing type [00:29:00] script project and just show how they could get better. And that's, I opened up the nozzle repo to him to try that out. And so he ran his scripts on all the nozzle stuff to do what we just talked. Solidify all of the existing types as these are typed as something, whether that's any or whatever.

[00:29:19] Tanner Linsley: Just make sure everything is typed , so that we can turn on all the strict nu checks. And we ran into a couple of barriers where he's like, Oh, I need to make my script a little bit better here. And I'm like, And our code sucks here There's a bad interaction between, bad code and a library that's still growing.

[00:29:36] Tanner Linsley: Yeah. But I feel like that is a tool that would be really helpful. Or just any tool like that, even for existing type script libraries to just say, go through our code. and just find these things that like a type script compiler might not be able to find, but something with a little more context, and some opinions could definitely could definitely find and [00:30:00] improve just automatically.

[00:30:01] Tanner Linsley: . So that's something that I would like to do. I feel like while I'm waiting on the back end schema types, I could at least go through and just manually put any. Anys anywhere, and make it so that I can just turn on all the strict stuff , And that would at least, that would at least get me to the point where, you know, oh, I'm making changes as I'm developing and I can see the type script output for the code that I'm working on, instead of turning on the compiler and getting literally thousands of TypeScript errors from tsc, just Oh, go.

[00:30:38] Tanner Linsley: Yeah. It's all about productivity.

[00:30:39] Joe Previte: Exactly. Yeah. It's yeah, and hopefully Josh's tool, he had mentioned it to us briefly, like when I chatted do you remember the name? It's like a code, the code module tool, or it's I can't remember what

[00:30:49] Tanner Linsley: it's called. Let me look it up.

[00:30:51] Joe Previte: But yeah, you're right. It's about productivity

[00:30:53] Tanner Linsley: and type stat. Okay. It's, maybe it's on Joshua k [00:31:00] Goldberg on Gith. Slash type stat. Oh, I see. Okay. We'll add a link in the show notes. Yeah, we'll add a link to that. Yeah, it says just trying, anybody listening, convert JavaScript to type script and type script to better type script

[00:31:14] Tanner Linsley: It's really cool. So let's see. It says modifies TypeScript types and existing code built in Mutators will only ever add or remove types and never change your runtime behavior. It can convert JavaScript files to type. Type script, ad type script types on files freshly converted from JavaScript to type script.

[00:31:33] Tanner Linsley: It can infer types to fix. No implicit any and no implicit. This violations annotate, missing nulls and undefined to get you started with strict null checks. So that's what I was talking about there. . Yeah, it's okay. Wow. I need to give this another shot. I think. Yeah. This has inspired me to reach out to Josh again because this really needs to happen.

[00:31:54] Tanner Linsley: For me, for and nozzle. I'm just itching to just have the strictness [00:32:00] back. Yeah. I'm working in my libraries in React Query and React table right now, and it's just like everything's strict all the time. If there's one thing outta place, everything blows up and I love it. It's the only way.

[00:32:13] Tanner Linsley: It's the, it's something like, how can you work like that? It's no, it's the only way to work now, because when I don't have that in nozzle, I'm just scared. Like I, I'll make a change and I'll hit save, and I'm just like Hope I, It didn't blow it up. . Yeah, I think it works. You go and test it, you go and experiment, you open up your browsers and you're like clicking on things.

[00:32:32] Tanner Linsley: You're like, seem, It seems to work . Yeah, exactly.

[00:32:36] Joe Previte: Wow. I wonder if you used types that on, like React query or like one of your other libraries,

[00:32:42] Tanner Linsley: what it would say? Yeah. Yeah. What would it say? I don't know. I bet there would probably be a few places where You know, where maybe like the generics, the generic nature of the library Yeah.

[00:32:55] Tanner Linsley: Has to be pretty loose in some places. And there's some areas where it's just we [00:33:00] cannot possibly know what this type is. Yeah. There's just no way. And we can't really make it a generic because there's just either no point in making it a generic because it's not reused externally. Or if you do make it a generic, then.

[00:33:16] Tanner Linsley: It's just an unknown. Anyway I don't know. I know that there's like nuance in libraries, you kinda have to pick your battles. . So with with a React table, there's a lot of generics extends this and Oh, that's another topic when you talk about is yeah. Generics in general, but let's

[00:33:31] Joe Previte: talk about it.

[00:33:31] Joe Previte: Yeah. Let's dive in.

[00:33:33] Tanner Linsley: Okay. One of them that really bugs me when working with generics is like when people talk about generics, They normally only ever have examples or imagine generics like with one or two generic slots. Okay. It's oh, here's a function and it takes an array, does something with the array or the items, gives it back.

[00:33:54] Tanner Linsley: There's only one generic variable in that solution. It's what is the type of the [00:34:00] item in the array, yes. Yeah. And then oh, but then there's also Like an accessor function that goes with it. Okay, we'll make that generic too, so that you can, utilize the generics there, but there's still only going to be one generic variable for all of that.

[00:34:16] Tanner Linsley: But what happens when you have like multiple generics, right? You start getting into function signatures where it's okay, this function accepts, a lot of different options, right? It's very high level function. Let's take use table, for example, from your active table. You've got generics in there for a lot of different things that the user can supply to the library that that are used throughout everything, right?

[00:34:43] Tanner Linsley: And it could be like the row type custom filters or aggregators, sorters that you've given it. There's a lot of like inversion of control that you can supply to the library, and those things need to be offered back [00:35:00] to you in the rest of the api. So all of them have to be generics.

[00:35:05] Tanner Linsley: And usually when you add a generic in one place in a library, it starts to creep everywhere. If you go to the source code for React Query, you're going to see generics everywhere. In fact, I'm just gonna pull it up really. Yeah. To give you a really good example we're just gonna go to React Query Source.

[00:35:27] Tanner Linsley: We'll go to the core, we'll go to the query client. Here's a line we can link to this in the show notes, but we've got a line here where fetch query, first of all, the fetch query method on. The query client has four or five overloads. I hate overloads with a passion , like they just complicate so many things.

[00:35:52] Tanner Linsley: But I hope, hopefully someday I can get rid of all these overloads and just have one way to use it. But if you [00:36:00] look through all these options on all these fetch query arguments, there's an option slot that just says fetch query options, and the type takes some generics and there's four of them.

[00:36:11] Tanner Linsley: There's T query function. T error, T data and T query key, because all of those things, we have no idea what they are in the library. We don't know what your data is, we don't know what the error looks like. We don't know what the data is that your query is returning, and you could return something different in your selector as well.

[00:36:32] Tanner Linsley: So we have to type that, like that's a generic, We try and infer all of these, right? . And then there's the query key, which. The query key isn't just like a string, it's actually like the arguments to your query function as well. So that's a generic, Those are just four of many other generics.

[00:36:48] Tanner Linsley: But if you look around this page, you're gonna see that same string like everywhere. T query data function, t data, T, T query key everywhere. Like every type takes those four [00:37:00] generics, . It really dirties up like it. It makes everything very like And this is just four in React Query. I have six or seven, or sorry, React table.

[00:37:15] Tanner Linsley: Yeah. I've been able to narrow it down to six or seven generics and they permeate everything. So imagine six or seven generics, you start to line wrap with prettier. Yeah. So everywhere you use any generic type, you're just like, you're already at seven or eight lines. . This is nuts. It's terrible. So like at that point you started asking yourself, what if we could just take all these generics?

[00:37:40] Tanner Linsley: This is what I was asking, and like generics are basically just Function parameters. If you were to think of a generic type as just a function, you're calling it with the, with the open carrots and then you're placing in your function parameters. What if we could call it like we, what an object.

[00:37:55] Tanner Linsley: You do that with JavaScript when you get more than a couple of arguments, you turn it into an options [00:38:00] bag. What if we could do the same thing with types and you can, it's like you can make this object that's called. T generics, . And this t generics object can have some keys inside of it that correspond to the generic types for those keys.

[00:38:17] Tanner Linsley: So now, instead of passing, four or five, or seven or eight, heaven forbid, to the generics, to all of your types, you can just pass one everywhere that you go, right? This cleans a lot of stuff up, but it introduces some complex. In your application around inference because the way that you infer types into that generic object has to change.

[00:38:45] Tanner Linsley: If any of those types rely on each other or have a specific order of inference, you can't just pop them into a generic object and have it all work because generic objects don't have partial [00:39:00] inference. You either have to, you either have to infer all of the generics that you pass, or you have to default all of them.

[00:39:09] Tanner Linsley: Like there, there's no middle ground. And this, there's a few issues open in type scripts that talk about this, like having partial generics, so

[00:39:20] Joe Previte: anything promising.

[00:39:22] Tanner Linsley: Yeah, like it's been worked on a couple of different ways that there's always these discussions around how do we.

[00:39:28] Tanner Linsley: annotate. What is a partial generic, right? And then there's also another camp that's and I'm in this camp as well, where it's okay, as soon as you have partial generics, that doesn't change. That doesn't change the syntax so that you can stop doing seven generics in one. They're slotted, they're basically array based.

[00:39:50] Tanner Linsley: They're index based as arguments. Okay. And what I want is I want partial generics. Inside of like object, bag style, [00:40:00] generic maps is what I call it. It's a generic map. You're mapping from, a key into a specific generic. And that's really what I want. So there's this, there's another issue out there for named generic slots.

[00:40:13] Tanner Linsley: So when you define a generic on something you. You would choose, like there's a name for the generic on the type that the original type defines. So if you have a generic that's Wow, I said generic too much. Starting to sound weird. . If you have a type that has, tons of generics and one of them is row, if you want to, if you just want to pass the type for the row, but have everything else be inferred, you could call, whatever the type is, and then you'd say row equals.

[00:40:45] Tanner Linsley: and then you would pass the type for the row generic and everything else would just get inferred. So like named generics, they have that, they have that idea for named arguments and other languages. We don't really have named arguments [00:41:00] in like JavaScript other than just like an options bag, you know where that's been.

[00:41:05] Tanner Linsley: But yeah, that's, those would be two, Those would be two things that would really improve. , the ability for like type script library authors to get really big with their generics and react queries. Not bad to be honest. Three or four generics isn't too difficult to manage, but React table got way too much and I actually did get React Table working today, the alpha version with a generic map version.

[00:41:40] Tanner Linsley: That's amazing. I love it. But it came with a lot of like weird hurdles around the inference. And I learned this from so many other types, group developers that are way smarter than me. But essentially you can create this generic structure that's an object, and. You have to split [00:42:00] up your inference stages, so you pass this generic object from function to function using closures.

[00:42:08] Tanner Linsley: And in the first one you can say, Okay, here's this type, and it can overwrite just a part of that generic map. And then you take that new. Overwritten type and return it and pass it to the next thing. . And then you get to call that function again with a new piece of information that you can then infer with not using the generic map, but just infer using classic generic arguments.

[00:42:33] Tanner Linsley: And then you suck those generic types out. You put them onto your generic map and you return it again. So you have to follow a flow of like generic dependencies, if you will. , like if generics depend on each other in some way, like in react table. A good way to think about it is that you have table options and then you have column definitions and you have and then the.[00:43:00]

[00:43:00] Tanner Linsley: Like the, your data that you're passing it, the data doesn't rely on any prior information, so you can pass, So first you provide the data type to react table. . Then after that you use the, you do the column definitions. You say, Here are my column definitions, but it has to be cured so that the column definitions can know about the type of data that you're passing it.

[00:43:23] Tanner Linsley: And then so you pass through all the column definitions. . And then after you've done the column definitions, then you can do the table options. And now the table options know about the data and your column definitions and any remaining options. And so you're you're using standard type script generics to suck out those types, but then you're putting them all into this generic map that internally you can get to just pass through one generic argument to everything that.

[00:43:54] Tanner Linsley: It makes it a little weird for people who come into a system like that, that [00:44:00] aren't used to types being declared that way because then they're like, Oh, cuz you, you still have to define some types in your own code. Hey, this is a row. Type. Normally they'd be like, Oh, Roe. And then there's one generic, right?

[00:44:12] Tanner Linsley: It's no, Roe has this generics object. And people are What is this thing? ? Yeah. Yeah. And it's okay, don't freak out, but for this generic, you're going to pass an object and then you're just gonna put, whatever you know about your data right there, . Oh man. And and sometimes you need to generate helpers.

[00:44:33] Tanner Linsley: React location also does this a little bit and it has some helpers some helper types that you can say, make table generics and you can pass it all the stuff and it will create a single generic type for you that you can pass to all of your stuff so that you don't have to keep, manually spinning up those type declarations everywhere.

[00:44:54] Tanner Linsley: You can just say for like free act table, you could say, This is my table. This is my row type. Here's are [00:45:00] my column definitions. Here's all the stuff. Make it into a generic map for me using a utility. And now you can just pass that generic everywhere, like export that table generic and you could import that in other places.

[00:45:13] Tanner Linsley: So if you have to like luck, hopefully everywhere, it would just be like supplied to you, but yeah, it's not always possible. Like the minute you break out a cell renderer to its own component, you're gonna have to def, you're gonna have to say the cell component, it's like, what is this cell thing that I'm getting?

[00:45:34] Tanner Linsley: It's Oh, it's a table cell and it takes generics. So now you're already into the camp where you have to provide some generics of some sort. Yeah,

[00:45:44] Joe Previte: man. Now I can totally see like how that can get so complicated so quickly.

[00:45:51] Tanner Linsley: It's a lot. And I wish I could show you. I had there's a screenshot on Twitter that I took recently.

[00:45:59] Tanner Linsley: Okay. [00:46:00] That, that showed all of the, that showed the type script core types. But with the old generic version, I'm trying to think. I can search for it after too. Yeah, there's a picture somewhere where I posted and it's like, Hey look, here's the new type script, stuff for React Table v8.

[00:46:20] Tanner Linsley: And somebody's That's a lot of generics, . And I'm like yeah, that is a lot of generic. So are people gonna be able to contribute to this code? ? Yeah. What do you think? Okay, I hope, but after I moved to the generic map, I got rid of a ton of lines, like it reduced the visual complexity of the code, like dramatically.

[00:46:43] Tanner Linsley: And I need to produce kind of a before and after version of that. Like I could do a, I could do a get diff to show that in fact. , I think I did send a tweet between me and. A mark. Oh, here we go. [00:47:00] No, I can't find it. We'll find it later. We'll put it in the show notes, but Okay. I sent him, I sent him the commit where I removed all of the granular generics and replaced them with that generic map, and it was pretty cool.

[00:47:13] Tanner Linsley: Yeah.

[00:47:13] Joe Previte: No, that would be awesome. Cool. Let's I'd say, let's wrap on, I guess one parting piece of advice for either like small teams, like you have at nozzle or library authors. Yeah. What advice do you have for those who want to use type script?

[00:47:29] Tanner Linsley: If it's greenfield, use type script from the beginning.

[00:47:33] Tanner Linsley: Turn on all the strict stuff, make it as strict as possible. Because even if you plan, even if you know that you don't have the ability to strictly type everything in your system, the power to opt out with any is good because at least you're opting out of strict. , it's a lot harder.

[00:47:53] Tanner Linsley: to opt into strict than it is to opt out. Like it's easy to put an any in your [00:48:00] code, but it's a lot harder to write new type script and have it be strictly typed while the rest of your code just is not. So start strict. Use any, don't be afraid to use any either. I use any all the time in our app code.

[00:48:17] Tanner Linsley: You can improve it later. Heck, I don't have types for a lot of my crowd stuff, so I gotta use any somewhere. That would be my advice. And for app developers, at least for library developers I would say get your types in line and do your types with your app. Like a lot of library developers are like, Ah, I'll just have the types be external.

[00:48:40] Tanner Linsley: , it's I don't know. I know there's edge cases to this, but I would say sure, you should probably rewrite in type script. The next time you do something big with your library, you should rewrite to type script. It's gonna change the way you architect it a little bit, probably, but it's gonna be for the better.

[00:48:56] Tanner Linsley: I don't know. My advice is just use type scripts. Don't be afraid of generics. Take [00:49:00] your time to get your generics right. Think about advanced concepts like, generic dependencies. It's like a, it's kinda like a source or I'd say like a graph model of like how your dependencies rely on each other.

[00:49:14] Tanner Linsley: Think about the developer experience that types can give your users instead of thinking about them as like restrictions, ,

[00:49:22] Joe Previte: Cool. Thanks so much for coming onto the show and talking to us all about migrating to type scripts. This was awesome. Any last

[00:49:30] Tanner Linsley: words? You can find me on Twitter, Tanya Linsley.

[00:49:34] Tanner Linsley: There's a Tans Stack handle. It's got an underscore in it cuz somebody's squatting you on Tans Stack . We let's see. React Table version eight will be coming out soon. It's a hundred percent type script. It's gonna be great. React Table V4 is in be. It's got some great new improvements. There's gonna be a brand new React query course coming out that I did with my partners@ui.dev.

[00:49:58] Tanner Linsley: My education partners.[00:50:00] It's gonna be fantastic, like literally the best learning experience I have really been through and. I went through and helped them develop all of the content for it. So a really cool official React query course collaboration. We don't even call it react query essentials anymore cuz it's not the essentials.

[00:50:19] Tanner Linsley: It is everything you're gonna need . And it's the same price as my old, probably. Terrible, Of course. . It's super outdated, totally. Yeah. So look forward to that. And yeah, I think that's,

[00:50:32] Joe Previte: Cool. Yeah. Thanks for all that. We will once that course comes out, we'll update the show notes with links and everything else you've mentioned today.

[00:50:39] Joe Previte: Yeah, thanks again for coming on. Yeah, you bet.