TypeScript Expert Interviews Bonus 11 exercises
interview

Tejas Kumar Discusses How to Build Bulletproof Apps with TypeScript

Tejas Kumar, a TypeScript and React expert, shares his experience with TypeScript.

He mentions that once developers embrace TypeScript and its "JavaScripty" approach, they appreciate its build-time protection, error prevention, and auto-completion features.

Tejas has built "bulletproof" apps with

Loading interview

Transcript

Matt Pocock: 0:01 What's up, wizards? I'm here with my pal, with developer advocate extraordinaire, TypeScript guru, React guru certainly, very, very nice man, extremely expression-full man, and ex-singing teacher like myself, Tejas Kumar. How are you doing, Tej?

Tejas Kumar: 0:17 Hey, it's good to be here. Hi, wizards. Wizards? You said wizards? Yes.

Matt: 0:21 Yes.

Tejas: 0:22 Hi, Matt. Nice to see you all.

Matt: 0:24 Wizards, witches, everyone. I'm super excited to talk to you about TypeScript because you've been working with TypeScript for a long time in application stuff. You've been doing talks on TypeScript.

0:35 You've been around the TypeScript community and involved in lots of different communities as well, so you have a wide field of view when it comes to TypeScript. I would love to hear a bit more about who you are and what your history is with TypeScript. Let's start there.

Tejas: 0:52 I'm Tejas. I started writing TypeScript pretty early on. I would say 2017, end of 2016. It was just like everybody else, in the beginning, it was very frustrating. I was like, "What is this? What are these errors?" I saw you made an explainer for the TypeScript errors. [laughs] That was way worse back then, and it was needed.

1:13 I started, and I knew it could provide value. I just didn't know where. Over time, the value became clear with autocomplete, with safety, with the build time compilation, and with all of that. Honestly, man. When I started, I was just like, "OK, the community says this is sexy. I don't know why. There's a build step. It catches errors ahead of time." That's what it was.

1:36 When I started, there was a lot of TypeScript that looked like Java. It was all classes, all the way down. Me coming from React and functional, that's one of the reasons I hated TypeScript early on, was it looked like Java. I didn't know you could use it the JavaScript-y way.

1:58 Because all of the examples, all of the docs were class this, class dogs extends animal, and things like that. So I was like, "Not so good," but I stuck with it, and I'm glad I did today.

Matt: 2:08 You came from React and then found TypeScript like it was something that was probably being thought-leaded at you and you adapted it to React and you started using it with React?

Tejas: 2:20 Yes, exactly. I've been writing React before that. Two years prior, I started with React, and it was all JavaScript. There was dot prop types and things like that. The concept of types was clear. When your prop types are invalid, you just see errors in the console. This whole being preventative of errors at build time was new.

Matt: 2:42 Yeah, and TypeScript since then has exploded in popularity. I saw you give a talk at Prisma in Berlin. I don't know, 2019, and you were saying, "Oh, TypeScript, this relatively hot new thing."

2:58 Look at the Stack Overflow, it's 48 percent usage. People are very happy with it. Now, it's 90 percent or something. Everyone's heard of it. What do you think has caused that? What's going on there?

Tejas: 3:09 That's such a good question. There's so many layers to it. For one thing, we can't ignore the capitalist competition, because until Flow from Facebook was being talked about, TypeScript was in the back room, chilling with a beer.

3:30 Like you said, TypeScript was thought leadered at me in the beginning. I used it, but also at the same time, Flow was thought leadered at me. At this company where I was working, we faced a long series of discussions about, "Do we go with TypeScript, do we go with Flow?"

3:46 Flow was arguably better than TypeScript for a time. It was head to head. It was neck and neck. Eventually, Flow ended up slowing down our project way too much to the point where we're like, "OK, well, this isn't helpful." It costs developer time.

4:00 We pivoted to TypeScript, but it was literally going from the frying pan to the fire, because we traded something that was being worked on by the cool developers at Facebook to this class-based thing that we didn't quite get. It was faster, but the errors were still unintelligible. It didn't feel that much of a gain.

4:20 I think the competition between Flow and TypeScript was what led Microsoft to invest in this and go like, "No, no, no, no, no, we need this. We have to win." That's one reason why around this time, the developer experience skyrocketed for TypeScript.

4:35 I think it was a competition. Also, it was an awesome coincidence of timing, because before Visual Studio Code, at least, I was on atom or WebStorm or one of these editors.

4:51 The combination of VS Code from Microsoft and TypeScript from Microsoft with IntelliSense and all of this, at some point, those came together. It was the most beautiful marriage of all time and it worked. At this point, Flow has no chance. I think that's some of the reasons why.

Matt: 5:08 Yeah, Flow is something like a stone, basically, in terms of adoption. I think one thing that is interesting about TypeScript is its stickiness. When people adopt TypeScript, they never want to go back. Has this been your experience too?

Tejas: 5:26 For sure, yeah. In the beginning, it was shaky. For those who don't subscribe to TypeScript today, for those who aren't indoctrinated into the religion, I feel like the benefits just haven't been clear because once they are, I find it impossible to go back because my code now breaks for me before it does my users. That alone is so valuable.

5:59 You add to that with auto-completion, with impossible states actually being impossible. Like if in an if condition, I put some nonsense, like if true equals equals equals, it just tells me.

6:11 Whereas in JavaScript, I've seen code bases that were full of these things. Code bases where a comparison was actually an assignment, and JavaScript was like, "OK, whatever. I don't care." TypeScript will be like, "What are you doing?" It's impossible to go back. It's like AI-powered JavaScript, literally is what TypeScript is.

Matt: 6:33 It feels like you have a helping hand. You're totally right about that because there's just a whole class of stupid syntactical mess-ups that we just never think about anymore. Like the single equals instead of triple equals, that never happens anymore or forgetting to call a function, things like that.

6:53 Were there things in React as well? Because you came from this React world. You were integrating TypeScript into React. Was there anything in React that made it particularly sticky for you that made it feel like, "Oh, this is amazing. I never want to go back."

Tejas: 7:07 Yeah, it was a build-time protection layer on prop types. I've passed so much nonsense to props in the past and then wondered what's happening, why is this not working? Oh, it turns out this prop was supposed to be an array instead of a string. Who would have thought, you know? Just working with React and seeing the red squigglies when I pass props has been super helpful.

7:29 Matt, the biggest value in the React ecosystem, comes from TypeScript. Not so much with React for me, but TypeScript with Redux. TypeScript in Redux and unit tests, I've created the most bulletproof apps with this combination. Whatever people use today, what is it? Like Redux Toolkit or Zustand or one of these state management, it's similar.

7:56 If you even use a Reducer, it would use Reducer in React. That, Reducers in TypeScript, unbelievable. I built a analytics tracking software recently. What we do is track actions on click events and things. It's beautiful because I have this very intelligent, discriminated union type of every action a user can do. It's just a massive type of unions of literally every action possible.

8:30 Then when you call a function track event, you pass in an event name that cannot be wrong. If you track click on call to action, you get that string and it's type-safe. You don't call, for example, kilk on call to action. You don't make a typo on these things. For analytics, this is really important because you aggregate millions of events into percentages and things like that...

8:53 [crosstalk]

Matt: 8:53 Fascinating. I've got a code editor here we could just dive into just so we get a concrete look at what you're talking about. You're describing if we have a type events, which would be, let's say, we export type CLICK, for instance, or CLICK_ON_SELL button or whatever, or CTA and then type, what's another example that we could use here?

Tejas: 9:25 That's good. This is a little bit simpler, but it's fine. There would be CLICK_ON_CTA, there would be CLICK_ON_LOGIN, there would be CLICK_ON_SUBCRIBE. Now, normally the way I've modeled this in the past is, it's just type CLICK, but there's additional attributes, like page and label and things like this. Exactly, yeah.

9:51 Page would be a string, and label would just be string. Yeah, exactly. You would track CLICKs. You would track NAVIGATIONs, so back forward. You would track text inputs. You would track all these things. Exactly.

10:10 It's a discriminated union because exactly for this. CLICKs can have pages, but when you navigate, you want to do something else. This has been so valuable. Now, if you have a function that accepts an event, there's no way you're going to mess up the call to this function that...Exactly. It shouldn't...

10:27 [crosstalk]

Matt: 10:27 End up with CLICK in one place or we call this, let's say dispatch, for instance, if we're inside a Redux thing, and we have CLICK there. Then when you pass in the type of CLICK, then you have to pass in label and you have to pass in page as well. This makes things so much easier.

Tejas: 10:46 Exactly. Now, imagine if page isn't string, but instead a massive union type of all your routes that you can generate and build time using fs.readdir.

Matt: 11:00 Wow, yeah, '/about', '/admin' and '/admin/users.' Let's say, you have this in one huge thing and then you even get beautiful to complete in here. This is almost like a union within a discriminated union, right?

Tejas: 11:13 Exactly, and now you will never track CLICK on some route that doesn't exist, if you automatically generate that union type. This to me is nuts. This is the value of TypeScript. It narrows your margin of error big time.

11:31 Imagine doing this in JavaScript? No way. In JavaScript, you don't have line numbers, but when you have type: "CLICK" in your dispatch, you could have type: "NAVIGATION." You could do NAVIGATION with invalid props and it would allow it.

11:46 Yeah, for this thing, it's been super helpful, because also now, let's assume this dispatch function stores these events in some type of data store, I can always make sure I will have type: "NAVIGATION."

12:01 Then I can aggregate all the navigations, like billions of navigation events, and there will not be typos or invalid data or anything like that. Yeah, extremely valuable.

Matt: 12:12 When you think of building, let's say, a state manager inside your application, you're thinking, "OK, I want to make sure my events are there and those events are type safe. I want to put the logic for handling those events in a reducer." Then are you unit testing those reducers as well?

Tejas: 12:30 Yes, definitely. I don't think types and tests are exclusive. I think they're complimentary. I will unit test everything in isolation as well.

12:40 One note, I've started doing this more and more often, I used to do types first. Before any TypeScript/JavaScript code types for everything and I would model flow, but because of Zod, I don't do that anymore. Because of z.infer, I define the Zod schema and then get a type from it.

Matt: 13:02 Interesting. There's a whole discussion there about, let's say that you're migrating something over from JavaScript to TypeScript. You need to somehow get these types through your system and you need to be able to express what these types are.

13:22 You're doing that or you're building something even from scratch, you said that you start in general with the types first or you start with the idea of the models first? I'd love for you to talk more about that process and how that works.

Tejas: 13:33 Sure. Historically, this is not migrating something from JavaScript to TypeScript. This is assuming I'm creating a massive state management solution or reducer with TypeScript. I would literally do type: "action" = and define that whole union like you did.

13:50 I would think of every possible action, because that's what reducers do. They respond to action dispatches. I would look at, "OK, actions. What do we got? Define a big union." Of course, it's not just a type, but then you'd think about the metadata that goes with each action.

14:06 I'm talking about an entirely event sourced like frontend architecture, where everything is an event being dispatched, not just one or two things. I'd literally start with types.

14:18 Once I have the types, I would write my reducer with the big switch case. I know it's safe and at the very end, I would probably throw to make sure that I've handled all the cases.

14:30 I'd cover it with unit tests after, but what I do now is instead of starting with that big discriminated type, I would write actions, like const action = with that. Instead of concrete values, it would be Zod schemas, z.object and all of that.

14:49 At the very end, I would export types, which is z.infer, generic, type of action, done. It's not a big change, but it does give runtime validation for free, which is what I would hope for people using JavaScript and who don't yet get the value of TypeScript.

15:07 That's totally fine. Everybody has their own journey, but I would really hope that people are, at the very least in JavaScript, using Zod for validation. When, and it is a when, the light bulb inevitably goes off, adding TypeScript types is trivial from there.

Matt: 15:28 That's really interesting that you think of Zod as, I don't know about an on ramp to TypeScript, if I'm putting words in your mouth or maybe you think about it in a similar way. What's the value of libraries like Zod as opposed to TypeScript?

15:47 Let's say, not opposed to TypeScript, but when would you choose to use Zod when you're valid to runtime? When would you choose to represent things as an interface?

Tejas: 15:58 I would do both. I wouldn't do them mutually exclusive, because we need safety. All applications need safety. You need safety at build time, at compile time, and at runtime. To just use TypeScript without Zod, I feel like it's half the solution, because you lose your safety at runtime.

16:16 To just use Zod is still half the solution, because you lose your safety at build time. The combination of Zod and TypeScript, that to me creates bulletproof apps.

Matt: 16:27 What things are you especially using Zod for? You mentioned actions in state managers. What other use cases would it have?

Tejas: 16:36 Everything. As I've said, every time I use the type keyword, which by the way, I do types, not interfaces. We can talk about why. I would actually love to get your thoughts on why there.

16:48 Every time I go for the type keyword, I stop myself and I'm like, "Wait, const identifier = Zod instead." Then I'll do type = z.infer. Almost every time I do this, because it's a force in function.

17:03 It forces me to think of, "OK, I'm probably going to need to validate this at runtime as well, as much as I need to validate this at build time,", but some things, maybe you don't. Some things maybe are not dynamic.

17:14 For static things, I think you can get away without using Zod. I would use Zod more for things that can change over the lifetime of my app, like network responses, user input, things of that nature.

17:28 That said, I still try to Zodify everything, because it makes me think of validation. Then it's almost like I opt out of Zod intentionally, then opting into Zod intentionally. That makes things safer for me. Zod by default and opt out, if I'm absolutely convinced a value is fully static.

Matt: 17:51 That's really cool, because I've got an article actually on when to use Zod, and my argument is you should use it on validation boundaries when things are coming into your app and you're not sure what they are, and that is still like an opt-in to Zod. You choose to use Zod as opposed to choose that the value is static.

Tejas: 18:10 The reason I don't do that is because if I have to choose to opt-in, I know myself, I'm just optimizing against myself. I know myself well enough that I'll do the thing that's convenient over the thing that's right. Especially if I'm rapidly iterating, I'll be like, "OK, I should do Zod here, but I'm not going to. In fact, let's just move fast, break things."

18:29 Then, over time, I just get lazy and I never have that runtime validation. By doing Zod first, it forces me with my own psyche to think more about validation from the beginning. Different people work differently. I think an absolute must is at -- what did you say? -- is at IO boundary.

Matt: 18:50 Yeah, validation boundary. IO boundaries, exactly.

Tejas: 18:53 Exactly, network user input, etc. That is non-negotiable.

Matt: 18:59 Your take on that is, "I don't trust myself in general."

Tejas: 19:02 Yes.

Matt: 19:03 Do you know what I mean? "I don't trust myself as a developer." For me, what I've noticed is the more experienced people are, the less they trust themself in general in development. It feels like TypeScript is a part of that journey as well because by adopting it you're saying, "I don't trust myself enough. I need more guard rails." Does that sound right to you?

Tejas: 19:25 Yeah, I don't trust myself. I don't trust my team. [laughs] Yeah, quite. We're human beings. What is the point of computers if not to pick up where we can't lift? I fully agree. We have to be well aware of our own limitations and employ the use of tools literally to help us.

19:48 Can you imagine if people try to build buildings and go, "I don't need a crane, I'll just like lift this huge stone up to the sky"? That's the equivalent of not using TypeScript to me.

Matt: 19:58 Nice. When you're on a team, let's say, because you've led teams with TypeScript engineers in the past, too, at what point is TypeScript integrated into your setup? Are you running on CI? Are you running it on pre-commitment? Are you running it on dev server? What's happening there? What's your current recommendation?

Tejas: 20:19 Yeah, that's so good.

Tejas: 20:21 That's a really good question. This has changed a lot over time. For us, the language server is running all the time on VSCode. We have created libraries in the past that were so small that we don't even have a build step. We just TSC done. We've also created websites this way. The build step is TSC.

20:45 If the build step is a bit more convoluted, then we'll do TypeScript as well along with the build, just to make sure the types work. We also run it on CI. I don't know. The cost of type checking hasn't been so expensive that we've opted out of it anywhere.

21:00 Local while coding, on commit, on build, and on CI has not been a problem. Maybe I just haven't worked on projects with such large scale, but especially with incremental, it hasn't been very expensive.

Matt: 21:16 That makes sense, because there's a few different places. There's an argument that you should be able to...and the thing I don't like when some people do this, I would hate to be on a team where this was the norm, where let's say I'm working on something and I'm just trying to get something to work.

21:36 Let's say I'm writing reducer and I'm just trying to get the types functioning or just trying to get the runtime stuff functioning. I just want to test that something's working. I know I haven't fixed the types yet, especially if you got the annoying no-unused-variables rule or something like that. Should you block the dev server if the types aren't correct?

Tejas: 22:00 That's a good question. You probably know the technical term for this, but TypeScript has...What is it? It outputs even if there's errors. This is called something. I used to know this. Basarat taught me this.

Matt: 22:14 Is it no emit on error or something? I can't remember.

Tejas: 22:18 Actually emit on error. That's the default behavior. If you have compile errors, it just emits anyway. That model is also applicable to dev servers and literally everything, because nobody cares if you have type errors, except your CI should care.

22:31 If the question is not so much when to run type checking, but when to block on a TypeScript error, that's a whole different conversation. What I've done in teams in the past is block on CI, but nowhere else, not on local, not on commit, because developers have eyes.

22:51 I've literally seen them try to commit something, and they see the reds quickly in the browser and they save it there. Worst-case, it's caught before code is shipped to production or even to a pool request preview. Then if somehow you miss it from your big VSCode window or your red file name, then you will see it on CI.

23:13 That's what I do. It helps to be fast. I can't imagine the pain of blocking hot module reload because of a TypeScript error. I wouldn't wish that on anyone.

Matt: 23:23 Yeah, brutal because you could block on pre-commit, which just seems awful because then you're running this huge tsc before pre-commit. You're blocking on dev server, which feels nasty. Blocking on CI sounds good, and definitely blocking before you run it on Vercel before you ship to production or Netlify before you ship to production.

Tejas: 23:40 Yeah, another point of blocking that people don't talk about as much, but I've seen is blocking on version, which is for npm libraries. Sure, bigger libraries will publish with some GitHub action or something, but there's usually smaller npm packages that you just publish from your machine.

24:00 When you run NPM version, it creates a git commit. Running tsc and blocking just before that has been super valuable because otherwise, you end up with a patch version that actually has an error. You just don't know. It becomes more complicated there.

Matt: 24:16 Yeah, it's really nice. I'd love to talk a bit more about testing as well and the role of types versus unit testing and how you see testing differently now since you've adopted TypeScript. From looking at the talk that you gave before, you mentioned that you tend to write fewer tests now that you have TypeScript. What's your attitude towards that?

Tejas: 24:46 Yeah, that's a good question. I don't think 100 percent code coverage is a good goal to aspire to. In mentoring some entry-level engineers, I don't know where this comes from, but everybody wants to, especially early on, go for 100 percent code coverage.

25:05 I've seen LCOV report, the code coverage reports where there's a function that's just not touched in the natural execution of the program. I've had entry-level engineers go and intentionally test those functions.

25:22 Then the only time those functions are touched is by the tests, not by the application, all in pursuit of 100 percent code coverage. What? When in reality, what's the solution? The solution is to delete those functions because they're never used. I have seen that happen a lot.

25:42 I've seen, especially before TypeScript, this idea of test everything. I don't believe that. In fact, TypeScript gives a lot more safety to where, you're right, I am writing less tests because I used to test for, or even in tests, I used to test input. I used to assert that a certain thing was a certain type and all of this goes away with TypeScript.

26:06 Now all I test is the things that I would like to pin their behavior down. Like in your package.json, you have the little caret and then the version number. Sometimes it can really mess you up. I've been bitten by this where it'll do a minor update. Then, "It was a breaking change. Oh, no, my thing broke. I hate my job.

26:26 Then how do you fix that? You remove the caret. You just have the version number. It's called pinning the version. I use unit tests to pin behavior only. I use basically all testing to pin behavior without accommodating for too much quote-unquote test micromanagement and without doing the job of validation as well.

26:49 It's just behavior. Probably the most convoluted test setup I ever had was I used to work at a company called G2i. I was responsible for building a system for automatically vetting code challenges. When you go to a job interview, they'll send you a take-home assignment. "Hey, build this app in React, whatever, use NX."

27:13 Then they used to send us a zip file. I built a system that reads the zip file, unzips it, npm install, executes the code, reads the code, and tells you what the person knows or doesn't know. Cross-referencing for React projects. It will go look at the React docs. What's covered here? Are they following the docs? How closely?

27:35 It will give you scores like this. Now you best believe that thing was unit tested to hack and back because you don't want to get someone's score wrong or something, especially the logic that does analysis on the source code. It had to read a React component and be able to confidently tell you this is a React component that does so, so and so.

27:59 For this behavior, you want to pin that down with a test. I'm not spending time testing inputs and outputs and types like this because that's what TypeScript is for. For those cases, I would do tests. Tests are expensive. I don't do them a whole lot, but yeah.

Matt: 28:16 It's like the tests you can stop writing are the ones where you're IO boundaries stuff, but it's IO boundary that's basically just you calling your function. It's like it no longer needs to be an IO boundary because that is guaranteed by TypeScript.

Tejas: 28:35 Exactly. Very well put.

Matt: 28:38 Thank you. The next topic I'd like to talk about is codegen actually, and the role of codegen in TypeScript. This is something I've not covered in my course actually at all, like generating code and the things that work on generating code.

28:58 I've used quite a few of them in my career, and I've seen that you have, too. What cool stuff have you seen out there in terms of generated code, and how useful has it been, and have you found any problems with it like Prisma does this, GraphQL codegen does this? What's your experience of that landscape?

Tejas: 29:19 That's actually one of the best uses of TypeScript is in codegen tool in parsers. Because if you look at programming languages, that's probably the most important thing to be strictly typed. It's like your abstract syntax tree. If you have an identifier, it has certain attributes and only those.

29:44 If you consider different types, like a variable declaration versus a function declaration versus a program block versus an identifier. These are different parts of an abstract syntax tree used in codegen and compilation translation. Each of those have different metadata.

30:01 If you mess that up, you mess up codegen, but you also mess up code interpretation. Your programming language breaks. That was actually my best experience with TypeScript was writing a codemod. A program that takes in one program and converts it to another library.

30:18 If you wanted to write a program that would take React code as input, convert everything to signals, and output Solid, you could do that. Type coverage for Babel or something similar that would parse and rewrite is extremely good. Past my time of doing this stuff, TypeScript grew and got better.

30:40 Now, correct me if I'm wrong, if you're working with strings, you can even substitute types in a template literal. You can use template literals for validation. If I was to think about codegen, I would explore those use cases as well. The whole Babel project and most of the parsers, excellent use of TypeScript. TypeScript makes that very comfortable.

Matt: 31:08 I've been down that road before as well. It's been super-duper nice because when I was working on XState, we basically built a parser that looked at the AST for...You had a state machine, let's say, and it would figure out the JSON from the AST and then turn that into an actual state machine and then be able to run it in VS code, so you'd be able to visualize it from there.

31:31 I remember the Babel experience being super good as well.

Tejas: 31:34 Yeah, exactly. One of my biggest struggles in TypeScript and weakest parts of TypeScript is creating a type...I don't know if this has gotten easier and I'm curious to hear from you. Speaking of codegen and more inspecting very deep syntax trees, is creating a type that can contain an object that can contain an object, like a recursive type.

32:02 I've seen this problem and I've solved it once before, but I can't remember how. I'm wondering if it's gotten easier to work with such types in TypeScript in the newer versions, or if that's...

Matt: 32:14 I can't remember. I'm not sure if I can give you a decent update to this, but if I create a new file here. Oh God, there's so much stuff open here. This is just my enormous playground repo with everything I've ever done in here with no commit history.

32:32 What you can do is something like this, where you have a type recursive, and then inside here you can say recursive -- Oh God, I can't spell. I really can't spell -- and then you would have recursive inside here. Let me see if this works. You'd be able to basically say const recursive, and this is almost like this...Wow, look at that. I really cannot spell the word recursive.

33:00 This basically just goes on forever, recursive, recursive, recursive. You have these self-referential types. This is actually a type that can never exist in JavaScript, funnily enough, because it would just keep on going forever.

33:12 It's almost like a type that circularly references itself. Now if you add a number, for instance, then this now needs to belong on every single level here. Is that the kind of thing that you're thinking about?

Tejas: 33:23 Yes, but a little bit more. What I'm thinking about is, so say, you have an object that is nested with 11 levels of nesting. What I want is a type that gets the keys of this object as a big union, but the nested pieces are separated by dot notation instead of...I just want keys. You know what I'm talking about?

33:48 That type was extremely painful for me to define back in the day. I did it by copying from Stack Overflow, but I'm curious to test you and your wizards' wizardry if that's gotten easier or not. [laughs]

Matt: 34:01 I feel like that's actually in a library that I saw recently. I'm not going to dig it out because I can't remember the name of it. It's crazy that some guy today, literally today, dropped a library, which was basically taking the entire type language and turning it into template literals.

34:20 It's like a version of Zod where it's called archetypes or something. It's a version of Zod where you basically say it can be a literal by just writing out a literal, or it can be string array by writing out string array. Template literals dropped in 4.1 have just enabled ridiculous levels of wizardry.

Tejas: 34:39 Yes. They didn't have the template literals back then, but if I was to explore deeply-nested object.keys return type, where each nested key is separated by a dot, I would look at the template literal types for sure.

Matt: 34:54 I want to go back to codegen because there's another interesting bit of the topic there, which is as a consumer of these tools when you're consuming the types of Prisma, let's say, or GraphQL codegen, how does that impact your velocity as a team? What does that look like in terms of adopting those tools?

Tejas: 35:18 It's been a mixed bag. If you know me, you know I've not said the nicest things about TRPC lately, but the fact that TRPC has no codegen as opposed to GraphQL, I actually like this because, with codegen, it's been mixed.

35:35 For example, I think of Apollo Codegen or really others. Prisma Codegen works best because it doesn't require a lot of watching. You define your schema. You run Prisma Migrate, which will then do Codegen if you have the Codegen plugin.

35:53 It will just work. It will generate your client types, everything's fine. That's a one-off thing. We've had problems with watchers of types that are generated that keep changing. One example of this is Apollo Codegen.

36:09 This is a long time ago. It's probably gotten better. I don't use Apollo anymore anyway because I haven't had a use case for it. What we had was, Apollo Codegen will scan your source files, your GraphQL queries, and generate types based on each GraphQL query.

36:26 If you remove an attribute from your GraphQL query, it will regenerate the types when you save, sometimes, or maybe it will regenerate them, but then VS Code hasn't yet caught up. There's race conditions with rapid Codegen of types that has come to haunt us a few times. That's been my experience with Codegen.

Matt: 36:47 That's very similar to my experience. I realized it was possible. I realized, wow, it gives you this Thanos-level, finger-snap power where you can inspect something, inspect a GraphQL file, inspect a database, let's say, and you get back all of this beautiful stuff.

37:04 The issue the more often those source files change, the more often your dev server needs to catch up, and then VS Code needs to catch up. Does that sound like...?

Tejas: 37:15 Yes, there's a lesson in there for people implementing libraries that use Codegen or for people trying to introduce Codegen to their teams. I think the lesson is you don't want to run Codegen often. Again, to echo what I said, Prisma does it masterfully. Only when your models change, really, then you regenerate things. That works.

Matt: 37:41 That works because that's less often. Also, when you do that, you're also affecting a database, right, because you need to tell that database to do the thing anyway.

Tejas: 37:50 Exactly.

Matt: 37:52 That's the difference. That model then of running Codegen less often is more effective.

Tejas: 37:58 Yeah, Codegen on save is probably bad. At least, it's hurt us because inherently writing to disk on save is a slower operation. Then everything needs to catch up. VS Code needs to catch up. The dev server needs to catch...all things need to catch up.

38:15 Sometimes they don't catch up on time. Dude, I've had so many problems with VS Code just not catching up. I've had to Command+Shift+P, Reload Window, and all of these hacks.

Matt: 38:31 When we use the word TypeScript performance, it's not about the code performance at runtime, right? It's what you're talking about. It's VS Code being slow. It's the tsc being slow. There's a danger that because TypeScript is written in JavaScript, it has a reputation for being slow, or occasionally being slow.

38:57 What dangers do you see to TypeScript? What's your experience of slow tsc and how it affects you?

Tejas: 39:03 I haven't been bitten by slow tsc too much. As I said, if you structure your project appropriately, the TypeScript team has done a lot of good work to make sure that it doesn't slow you down to a halt. Incremental mode being one of it. I'm a huge fan of incremental mode.

39:22 Since the day it came out, I will die on this hill. It's very valuable. If it is problematic for one reason or the other, then you could use incremental locally and on CI node. There's ways around it.

Matt: 39:37 Just to pause you there. Could you explain like I'm five, incremental mode, and what it does?

Tejas: 39:41 Sure. It keeps a .tsbuildinfo file in your project that is some type of -- I haven't looked into the .tsbuildinfo -- that is some type of cache that makes everything faster because it stores state that it can just quickly read on your file system. It doesn't have to re-scan everything. I hope I'm doing this right. You probably have a better answer than me.

Matt: 40:06 I genuinely don't, actually. I genuinely don't know how it works. I imagine it's like caching some operations, making sure you don't need to check every single file, and doing some caching.

Tejas: 40:15 Exactly. Whatever it is, it stores state for easy reference so that it doesn't have to...It seems to me -- and I'm probably wrong here. I would love it if some expert answers -- but it seems to me like it's storing cached inference results so that it doesn't need to continuously scan everything over again from the beginning.

40:34 Therefore, only scanning things incrementally is my two-dollar guess. I'm familiar with it as the tsconfig flag. I turn it on. I tell it where to put the .tsbuildinfo and it flies.

Matt: 40:49 That's sick. That's a really good tip.

Tejas: 40:52 There's a lot of rumors that -- they're not even rumors -- there's someone at Vercel who's writing a TypeScript compiler in Rust. I'm skeptical of this. I would love to see it actually be, what was it, Next.js got a 700x improvement on the tsc process.

41:17 I'm very skeptical because, thinking of the entire operation, I don't necessarily think Rust will make it faster, but I would love it if I was wrong. I actually had a long conversation with people about it at this conference I was at last week. We all agree that it's probably not going to work, but I hope it does. What was it? What were you going to say?

Matt: 41:40 I was going to say, because I've interviewed him. I interviewed Donny not for Total TypeScript, but for an article on Total TypeScript, which is free. If you're watching, you can have a read of it right now. It's STC, that's the thing. He's calling it Speedy Type Checker.

41:56 There's a lot of efforts to try and speed up TypeScript, and a lot of them are internal to TypeScript itself, so it's not just STC. Donny is trying to rewrite it from the outside, but what he's finding is that a lot of TypeScript's internals can't be parallelized, because there's so much in the global scope.

42:15 You need to check everything first before you actually understand everything that's going on, and then all of the globals, you can figure them all out. There's a lot of efforts to reduce the number of globals that are possible in TypeScript, and that way you can parallelize more work, which is interesting.

42:32 I think that's probably the way to do it, because if you do less work or do more work at the same time, then it's going to be faster.

Tejas: 42:39 Have you been bitten by TypeScript performance bugs? Not bugs, but has it been very slow to you to the point where it's hindered your productivity?

Matt: 42:48 When I was working in an enormous monorepo, I thought it was a good idea, and I still think it's a good idea, to have this huge monorepo where a lot of the stuff in the monorepo was relying on shared packages. The shared packages weren't built TypeScript.

43:09 They were just raw TypeScript files that were imported. It ended up being this huge, great, big project with lots and lots of TypeScript stuff. In that project, VS Code in tsc was running pretty slowly.

Tejas: 43:22 I can see that. I've never had a nice, pleasant experience with monorepos. Even today, I was working with a monorepo project. Some random "postcss is missing" message, which it wasn't, but I lost a good two hours to a monorepo today, so I understand.

Matt: 43:40 That's interesting. Let's finish by talking a little bit about TypeScript's future, and where you see TypeScript going. What do you think TypeScript's future is? What do you think the potential of TypeScript is? Where is it going?

Tejas: 43:58 I think TypeScript is going to be the de facto way we write apps. Some would say it already is. I would tell those people they're in a bubble. It's not, but I think it will be. I don't know if you saw, recently, Kent C. Dodds said, for everything he teaches, it's going to be TypeScript first, unless he's explicitly teaching JavaScript.

44:24 That's a big jump, and we're actually seeing this. More documentation for more projects will just be TypeScript out of the box, implicitly. This is new. This hasn't been this way, so I think the future of TypeScript, in many ways, is that TypeScript is the future of the web.

44:42 It will still be a superset, so JavaScript will still be the driver, but I don't see a reason why we would write things without TypeScript. Even if it's something as simple as a one-page website, a lot of people will say, "Oh, just do HTML, CSS, JavaScript." Sure, but if the assumption is that good and healthy things grow, then why not optimize from growth from day one?

45:08 People will misinterpret this and be like, "Oh, he suggested adding React for a login page." That's not the same, because TypeScript disappears at runtime, but it keeps things safe and scalable.

45:23 TypeScript has this unique superpower, where it optimizes for DX and UX while disappearing on the user side. I don't know of any other tool that will do that. That doesn't actually add to your bundle size but gives even the users of your bundles' benefits. For that reason, I would bet you good money that by the end of this year, most docs examples are just TypeScript first.

Matt: 45:56 More docs in TypeScript, more beginners learning TypeScript as their first language as well?

Tejas: 46:01 Yeah, I think so. There's a lot of existing JavaScript-only developers who are opposed to TypeScript. It's really because TypeScript has been misrepresented. This is where I have to call out and appreciate the work that Theo does because I think he has a video on, "You've been learning TypeScript wrong," or something like this.

46:23 The TypeScript wizards of us, we're huge fans of not just TypeScript, but extreme TypeScript. Let's do strict mode, no implicit any, no unused locals, let's go absolutely nuts. Then someone shows up and they say, "Hey, I'm not comfortable with TypeScript." We're, "What? Are you...?" versus starting a project with strict -- oh, my god -- and all the strict rules off and even anys and unknowns.

46:51 Anys not always a bad word if it's for teaching and helping people get on board. Then slowly explaining the value of, "Wait, if we change this any to a more concrete type, we get protection, we get auto-completion," and explaining it that way, I think will happen more and more as well to the point where TypeScript takes over more hearts and minds.

Matt: 47:12 Nice. TypeScript is taking over the world. TypeScript is everywhere. TypeScript is on the backend in nodes. TypeScript is on the front end. TypeScript is no more HTML, CSS, JavaScript apps, HTML, CSS, TypeScript apps, basically.

Tejas: 47:28 Also, TypeScript in WebAssembly. There's TurboScript, right, which looks extremely similar to TypeScript bar a few types and compiles to Assembly. Yeah, I think why not TypeScript everywhere? It's earned it. It's valuable. It's shown its value.

Matt: 47:48 For sure. Any final thoughts then before we wrap up on...? That serves as a pretty decent final thought, TypeScript's future. There's TypeScript 5. coming up as well, which is bringing out a few new features, bringing decorators. What are you excited for in TypeScript's future? What are you hoping they'll add? What would you like to see from the TypeScript team?

Tejas: 48:14 The one thing I'd like to see is two things. One directly from TypeScript, one not. The thing that I'm really excited to see from TypeScript in the near future is even better output of error messages. They still struggle with this a little bit. I do see it getting better and better.

48:33 That's definitely something I will always like to see from TypeScript until it gets better. By that, I even mean they do a good job of having TS, and then a number as a suffix for an error code. Imagine if TypeScript errors had a link in them, "Click this, and you go to a playground. We show you how to solve it," or, "Here's what I want."

48:52 Matt, this is exactly what I want. I want TypeScript's errors to look like Rust's errors. I don't know if you've seen Rust. They're just beautiful. I wish they contract out the Rust team to come in and show them how to do errors.

49:07 [laughter]

Tejas: 49:07 It's so good. Really, Rust will give you links. They'll teach you how to solve it. They'll explain it in plain English, "Here's what you do." It's phenomenal. That's what I want for TypeScript. Also, outside of the power of TypeScript, what I'm really looking forward to is, I believe, your VS Code extension. Has that been released? Can I use it for...?

49:29 [crosstalk]

Matt: 49:29 It's been slowly released, yeah. It's out there.

Tejas: 49:33 I think I will use it more heavily as I teach people TypeScript, which teaching is a huge part of what I do. Those are the two things I'm really excited about in TypeScript and in its ecosystem.

Matt: 49:40 Awesome. Tejas, thank you so much for coming along. It's been so cool to chat to you. You know what, there's one more thing I wanted to talk to you about since we've got five minutes left, which you're a little bit anti-tRPC, or you're not even super keen on it.

49:57 [laughter]

Matt: 49:56 Let's finish there.

Tejas: 49:58 Wow, what a place to finish. This guy is leaving me under the bus.

Matt: 50:01 Let's go with some vitriol for the end, yeah.

Tejas: 50:04 I'm not anti-tRPC. In fact, I'm really good friends with Christopher Ehrlich, one of the maintainers. I'm not anti it. I am anti-esoteric-ness. I am anti-easy-migrate-ability. This is my big bone with tRPC -- because, look, Create T3 App is awesome. If you're starting a greenfield project, great, tRPC helps.

50:31 Where it doesn't help is if you have an existing application and everybody and their mother around you is singing the praises of tRPC and Jack Herrington is, "You've got to get on tRPC." Then you try and you can't. Then you go talk to a maintainer and they say, "No, just start a greenfield project."

50:46 For me, what, that doesn't help anyone. I keep talking to Christopher. I'm, "Bro, when are we going to make it easy to help people migrate?" That's a conversation that's happening. That's really what will also set tRPC over the edge because not everybody is starting greenfield projects all the time.

51:06 Another huge thing I'd like to see for tRPC is just solid documentation. What I'm about to say people will not like, but I think tRPC struggles with the same developer pain points that Angular does. Angular 2 specifically. Angular is becoming better now. I don't know if you saw their new updates. It's getting pretty good.

51:26 There's a new thing called standalone that is awesome. Anyway, the problem of tRPC and Angular 2 around is similar. That is concept overload, and tRPC mix, from what I've seen so far, little effort in the docs to explain what's happening, how you wire things up, "This is a router."

51:48 There's just a lot of concepts that you get maybe out of the box with Create T3 App. The docs could be a lot more clear about what happens where and how to wire things up such that when you want to migrate something to tRPC yourself, you have a more comfortable onboarding experience. I'm not anti-tRPC. It struggles in communication a little bit.

Matt: 52:10 We've got a mission statement for tRPC, a mission statement for TypeScript. This has been awesome. Thank you so much, Tejas.

Tejas: 52:17 No problem.

Matt: 52:17 This is always so cool talking to you. Thank you so much for joining.

Tejas: 52:23 Appreciate it.