Migrating a Legacy JavaScript Codebase to TypeScript - JSJ 680 - podcast episode cover

Migrating a Legacy JavaScript Codebase to TypeScript - JSJ 680

Jun 05, 20251 hr 26 min
--:--
--:--
Download Metacast podcast app
Listen to this episode in Metacast mobile app
Don't just listen to podcasts. Learn from them with transcripts, summaries, and chapters for every episode. Skim, search, and bookmark insights. Learn more

Episode description

In this episode, Dan and I (Steve) dove deep into what turned out to be a surprisingly complex, yet incredibly insightful topic: gradually migrating a massive legacy JavaScript project over to TypeScript. We're talking about nearly 1,000 JS files, 70,000+ lines of code, and years of developer history—all transitioning carefully to a typed, modern future.

Dan walked us through how he started by setting up the project for success before converting even one file—getting CI/CD ready, setting up tsconfig.json, sorting out test dependencies, dealing with mock leaks, and even grappling with quirks between VS Code and WebStorm debugging.

We talked tools (like TS-ESLint, concurrently, and ts-node), why strict typing actually uncovered real bugs (and made the code better!), and why it’s crucial not to touch any .js files until your TypeScript setup is rock solid.

Key Takeaways:
  • Gradual migration is 100% possible—and often better—than ripping the bandaid off.
  • TypeScript can and will catch bugs hiding in your JavaScript. Be prepared!
  • Use VS Code extensions or TS-Node to support your devs’ tooling preferences.
  • Don't underestimate the setup phase—it’s the foundation of long-term success.
  • Start small: Dan's team converted just one file at first to test the whole pipeline.
If you’re sitting on a legacy JS project and dreaming of TypeScript, this episode is your blueprint—and your warning sign.

Become a supporter of this podcast: https://www.spreaker.com/podcast/javascript-jabber--6102064/support.

Transcript

Speaker 1

Hello, everybody, Welcome to another thrilling episode. I was going to say, example episode of JavaScript. Jower, I am Steve Edwards, the host with the face for radio and the voice for being a mine, but Dan got to me the host, so you're stuck with me speaking of panelists. Was I speaking of panelists?

Speaker 2

Now I am?

Speaker 1

Anyway, Dan Shapiro is here with me. We are flying solo today. How you doing, Dan, I'm doing great.

Speaker 2

So let's do our usual weather report.

Speaker 1

It is cool and rainy here in Portland, shocking for Oregon.

Speaker 2

And in April, I know.

Speaker 1

How tel Aviv guess warm and sunny?

Speaker 3

How did you ever guess? We literally didn't really have a winter this year, to be honest, it was more like a bit of a fall. Not that you know, we should have more rain than we did. But you know, it's still lovely there at least that and it still is lovely weather fabulous.

Speaker 2

Well it's fabulous for you, it's jealous for me.

Speaker 1

But anyway, so today we are going to be talking about a practical application of pipescript in JavaScript. I'll let Dan give the details, but we're going to talk about everything that was involved in Was it a new project I already forgot. Oh so yeah, I'll let you talk from here so I don't mess it up anymore.

Speaker 3

All good. So basically I had this interesting situation at work. We have a large legacy JavaScript project that's still a core part of our offering, and it's under active well beyond maintenance. It's being actively developed and enhanced and improved and whatnot. But it's all done in JavaScript on hundred percent JavaScript because it was started more than eight years ago and it was never moved over to typescript. It just stayed JavaScript, and I was looking at introducing typescript

into that project. Actually that project has two parts. It has a server that's just running on no JS with Express, and it has a client or front end side, which is implemented in React. That part actually again talking legacy. Some years ago before I joined the company, they made the kind of unfortunate choice to introduce flow into the front end part for type safety, and they only did

it partially. I didn't want to touch flow yet, so I was looking at introducing type script to the back end part, to the no JS part of the project, and it turned out that it was much more complicated and error prone than I expected. There were a lot of pitfalls and gutchas along the way. I kind of thought that, you know, given that the whole idea of typescript is to get JavaScript devs onto the bandwagon, that it should be really easy and straightforward, and it wasn't.

And I even consulted with some typescript luminaries like Matt Polcock had a lot of great advice and suggestions, but it still wasn't easy. Also with Jack Harrington and others, they all had great advice, but it still was much more challenging than I expected. And the end result is that I kind of documented the entire process, did a bit something like an internal blog about it, and we

have this internal wiki. So I wrote the whole doc about how to do it so that if and when we do it in another project, it'll be much easier that time around. And I thought that it would be interesting and useful for our audience for me to show this information. I hope that it is okay.

Speaker 1

So just to clarify, we're on the front end, you're still sticking with what was there, right, In other words, you're going to go with the flow, should we say. And then on the back end was where you're doing your typescript upgrading.

Speaker 3

Yeah, So the answer is actually that our ultimate intention is to do typescript on both sides, but I wanted to start somewhere, and it seemed easier to start with the just JavaScript part because Flow and typescript, while they are similar, they're not the same. They're not compatible, and I didn't want to get into the whole mess of how I get typescript and Flow working together in the same project.

Speaker 1

Oh you so you wouldn't. You wouldn't replace flow with tithescript.

Speaker 3

I would, But these are large projects, so replacing well, let's put it this way. I really wanted to do

everything gradually. It's important to emphasize this point. So for example, talking about just to give an an understanding of the size the scope of the project, So talking just about the back end part that no JS project, we're talking about close to one thousand files JavaScript files, about seventy thousand lines of code, something like thirty five hundred commits since the project started over seven close to eight years ago.

So it's not a small project. And obviously I can't move all the JavaScript files over to typescript, all thousand of all one thousand of them in one go. Now, there are some automated tools that automatically move all your JS files into TS and add types as best they can, using all sorts of tricks and static analysis and whatnot. I did not want to do that. I was looking. The way that I wanted to go about it, and especially after I spoke with some people, is to enable

a gradual migration. And I'll talk about the two ways in which a gradual migration can be achieved. By the way, if you want to understand why I'm looking at a gradual migration, why I prefer this approach, you can also listen to the interview that I did with Matt Pocock a while back about typescript, and we also discussed this aspect about migrating projects, although again we did not go into the exact details that I'm intending to go into today.

So the idea was to basically introduce typescript into the project so that we can do the actual migration of the files slowly over time. It could literally take years. Is that is that clear? I hope? Yes.

Speaker 1

Sorry, I was looking at the Matt Pocock episode so I could tell people what it was.

Speaker 3

Yeah, we can probably put a link in the show notes. Yeah. Yeah, it was an excellent episode, highly recommended. Wait a minute, we did two episode with them. Are you looking at the latest one, the one from like about a few months ago, or an older one from like two years ago?

Speaker 2

Uh? Yeah, five thirty eight.

Speaker 3

It was probably a couple of years ago, so we did more recent one.

Speaker 2

Okay, we'll find those and mention them later.

Speaker 3

Okay, So again we're talking about one hundred percent JavaScript product built on top of NOGS with Express. Like AJ likes it, it uses common JS rather than import, so it requires it's a legacy project for an API with the front end side, it actually uses a combination of Swagger and open Api for public RESTful APIs and actually

graphql with Apolo for the internal private APIs. For unit testing we used Moca and Chai and it has end to end tests implemented mostly using Playwright, but that's less relevant. As I said, almost one thousand JavaScript files, almost seventy thousand lines of code, and around thirty five hundred commits

since the project started. And again we use squash, so it's really thirty five hundred prs really rather than you know, along the way the world a lot more commits, but they were just squashed together.

Speaker 1

By squash you mean, not the vegetable. But when you do the get merging in, you squash multiple commits and do one bigger commit exactly.

Speaker 3

Just a clarify for yeah people, because you know, there are a lot of small commits along the way. I'm fixing this. I'm fixing that they're they're not that interesting in most cases. You know, you can keep them if they're really important, but in most cases they're not that interesting. Okay, So I was looking to enable a gradual migration, which means introducing Typescript into the project, but not actually moving the entire project over to typescript. Maybe a few words

about why I wanted to do that. It's really funny. I also spoke about this with Matt. When typescript first was introduced, I had a kind of a negative attitude, you know, when I came to JavaScript. Statically typed languages like C plus plus and like Java, like C Sharp and JavaScript. The dynamic nature of JavaScript afforded an amazing

amount of freedom, and I loved it. But as I became ever more seasoned in JavaScript development, especially working in larger teams, I came to realize and understand that for larger projects involving larger teams, for production grade software, you want something that's more structured and rigid, and you want something that helps you avoid certain errors that can easily

happen if you're just using JavaScript. You know, the obvious example is you just misspelled or mistype a property name and you just get undefined instead of the actual value. But there's no error in any other in you know, in any other way, and it seems to run. It just runs wrong, And that's one of those things that

typescript protects you from. Typescript. Also, the code is much better, much more self documented, especially if you're talking about APIs, Especially again if you're talking about larger teams that compose software from parts, components or services together. You want APIs

that are much better structured and self documented. And the integration with modern development tools like vis code especially that does the much smarter autocomplete for you and puts squigglies if you pass in wrong parameter types or the wrong number of parameters or whatever. All these are super useful tools when you're working, especially as I said, in an enterprise type environment. And these days it's also better with

AI because it gives the AI greater context. By the way, all that being said, if I need to solve a lit code type problem, I'll probably just do it in JavaScript and not mess about with the types. But for everything else, I find that even though we are writing those types that ultimately are evaporated out of the code, because when Typescript is compiled to JavaScript, it basically just

strips out all the types. Despite that fact, I still find it incredibly useful maintenance for the development and maintenance of larger certainly of larger code bases, and especially in this case, the fact that the project was done in JavaScript and not Typescript was definitely hurting our developer experience.

Just recently, somebody literally introduced a bug that could not have happened had the project been entirely done in Typescript, and it didn't propagate all the way to production, but it was only caught by an end to end test, you know, later down the line, and it was much more difficult and expensive to handle than if vis code would just have put a squiggly under that offending instruction.

Speaker 2

So far, so good, so far, so good.

Speaker 3

By the way, I totally appreciate the fact that a lot of front end developers or full stack developers still preferred to work in JavaScript. You know, you do you whatever works best for you. For me, after all these years, I've come to the conclusion that again, for the larger projects involving multiple developers, certainly I prefer you working in typescript. Okay, So I have the project, it's pulled down to my computer,

and I want to start. The first thing that I went about, again because it's a legacy project, is I want to upgrade, to make sure that node is upgraded to the latest possible version, ideally to the latest LTE version LTLS version sorry, long term support version. And if people are curious about what that means. We had that conversation with the node guys about the nine pillars of note.

Hopefully you're using something like NVM, the node version management, so you just you know, find the latest version you support you can support idea, the latest LTS version and put it in your NVMRC file. Update to that and make sure that that it runs and that you can work with it. Also make sure that obviously that you were pulled on the latest version from you know, GitHub or whatever, and that it appears to work properly in

your environment. The next step is to make sure it seems obvious and explain why it isn't that your unit tests work correctly. Now you might say, well, obviously, because otherwise the project will be broken. But in our case, at least, it turned out that some of the unit tests had cross dependencies. What that means is that they used mocks but didn't always properly cleaned the mocks up the results. Some of the tests actually only passed when they were run in a particular order.

Speaker 1

No, yeah, I've ran into that before. I know exactly what you're talking about.

Speaker 3

Yeah, And it turns out that everything seemed to be fine because when it was run in the JavaScript project, it was it happened to run in the correct order. Otherwise people would have noticed and fixed it a while ago. But when I actually started moving the project over the typescript, it turned out that because of the transpilation step, things didn't run in exactly the same order, and then certain

tests broke. Or I had certain situations where I needed to touch the business logic, and then I wanted to run particular tests individually, and I found out that when I tried to run the test individually, they failed, and then I went back to the original code and founded those tests. Even when I ran them individually in the actual original JavaScript project, they also failed because they only passed when they were run in a batch in the appropriate order, because again of mock leaks. I won't go

into the detail of what mock leaks have are. Basically, it just means that a mock was put in but wasn't properly removed at the end of the test. It consequently leaked into the next test, uh, and it impacted that and in that case it actually enabled it to complete successfully. And when you just run that test on its own, it doesn't get those mocks, and then it failed.

Speaker 1

So in all the idea, I mean, obviously, I think the idea there is that tests should be self but they stand by themselves. I don't know what the correct term is, right, so you're not dependent upon other tests.

Speaker 2

Yeah, for the test to spin up.

Speaker 1

Your environment and give you a mock up and give you everything you need, run the test, break it down on the next test.

Speaker 3

Yeah. By the way, even the fact that you know a lot of the code has that so many marks were used were test statement to the fact that it was legacy code, because ideally, in more modern code I would use dependency injection and try to avoid so many marks. But again, the code is what it is, and that

was the situation. So basically the thing was that before I could actually start the migration to Typescript, I actually needed to fix a bunch of mock leaks and get the tests to run properly in the original project before I even start migrating it over to typescript. So I verified that all the tests could run individually, and only then did I actually start the migration process. The next step,

of course, is to install Typescript and initialize it. So you do youless you're using NPM or yarn or whatever. You NPM or yarn or MPM installed typescript, you do Typescript in it. And already here I ran into an issue because my default assumption was that the installation of Typescript should be as a dev dependency, because why would you need the actual Typescript compiler in the production environment.

It's just part of your development pipeline. In order to actually build the system, the installable part of your project. You only actually deploy the disk folder or whatever it is that you're creating. And it turns out that at least in our case and maybe also in the case of our listeners, that was not the case. I actually had to install it as a PRAD dependency. Are you familiar with the difference between a PROD and a DEV dependency?

Speaker 2

Absolutely?

Speaker 1

I mean we ran into the same thing, uh in the PHP world on the back end with composer, where a classic a similar analog to what you're doing is PHP stand which is a static analysis tool for PHP. Where you don't want that, you know, in your in your PROD build, so you put it as a dependency use a save DEAV flag.

Speaker 3

Yeah. So unfortunately, I actually had to install typescript as a PROD dependency in our case. And the reason was that the build. The way that the built pipeline worked is that it actually created a pod for deployment, uh, you know, like a Docker pod basically, and and it actually did sort like a get pulled inside the pod

and then ran the build in the pod. So if and it only did and it only ran the PROD installation, so if I didn't put it as product PRAD, I actually broke our CICD pipeline because it tried to run the TC as part of the PROD build, and it wasn't there. Now, ideally I should fix the CICD pipeline so that it doesn't work this way, maybe build it into a special environment and then copy the files over into the PROD environment rather than build in the PROD environment.

But I wanted to reduce the amount of DevOps work that I actually had to do, and it seemed just a lot easier to just make Typescript itself a PROD dependency. It's just an ideal, of course, because you're shipping Typescript as part of your note modules. It's broad installation. But you know what, it's so big anyway, it didn't make that hole that much of a difference in our case. And as I said, I just wanted to get over this this blocker, so just.

Speaker 1

So just to clarify that, so it doesn't you know, I'm not a big Typescript user myself used some and not a real big user of it.

Speaker 2

But correct me if I'm wrong.

Speaker 1

But doesn't the whole point of the typescript compiler that, as you you know, you write your typescript code and then it compiles. It does I was checking it saves it to job script files, and that's what you want to build off of, isn't am I crrect?

Speaker 3

Yeah? Look, there there are really two ways these days in which you can run typescript on top of note, the way that you usually want to do in production environments is you want to run the Typescript compiler TSC. It runs, it does all the X, it fails if it identifies an error, but if everything is fine, it general like you said, it generates JavaScript files from the

typescript files. So for every dot TS file you get a dot js file, and in that JS it's basically just essentially looked mostly like stripped out the type information. And then ideally you would just deploy those JS files and you don't deploy the TS files. But as I explained, the way that our CICD pipeline was built, because it was originally done before TSC, nobody thought about about that.

The way that it worked was that it actually did a Git pull directly to the production pod that it was building, and then ran a quote unquote build on it, which effectively did very little in the pre TSC scenario. In the TSC scenario, that build actually runs the TSC compiler. So building the production pod required having TSC on that pod,

so again not ideal. A better solution would be to fix the CICD pipeline, but that's how it was built when it was built, and I didn't want to mess about with the CICD job and building new one or whatever. As I said, I don't consider myself a dev's off person, and if I can avoid doing that, and I prefer to. By the way, these days, there's a different you can there's a different way for running typescript on top of Node.

Actually there's a couple of ways. So first of all, there's this project called tsnode which literally wraps Node and adds TSC some support for typescript files into Node by effectively running TSC when it loads a file when files are loaded into Node, so you can use tsnode to run the typescript files directly on note without having to use TSC first to build the jobscript project out of it.

But obviously this is not something you really want in production environments because all that thing adds a lot of overhead when the Node process starts, and why pay for that overhead. What the Node team has recently done is they've actually added a new command line switch, which kind of quote unquote gives Node TS support. The way that they do though, is not by running TSC, is by

stripping out the type information. So think about like it's it's overwriting all the type definition with spaces, so it's kind of evaporating the type information out of the out of the TS files and then what you're left with is just JavaScript and it runs that, and it can do that fairly quickly. But on the conversely, it doesn't actually do any type checking. It just so they expect you to do the type checking beforehand, but they say you can still run the TS files directly and you

hardly pay any over any overhead costs for that. Is that clear?

Speaker 2

Yes, sir, good.

Speaker 3

But again this is variatively new, relatively recent, and when I worked on that project, I didn't use it, so I I really needed to compile the TS file into JS files. So as I said, I installed TS, I initialized it, but I put it as part of the pro dependency, not a dev dependency because of the way in which our CICD pipeline is built. When you install an initialized when you initialize typescript, actually it generates a TS config file or TS configured dot json file to

be precise, at the root of your folder of your project. Sorry. In fact, that's how TSC knows where the root of your project is. It assumes that where that file is located is the root of your project, and that ts configured dot json files is a JSON file obviously of configuration data for TSC telling it how to work how to process your project. For example, you can tell it

which folders to include or exclude from its compilation. In our case, all the source files except for the root index file resided in an src folder, and all the test files the unit test files, and also some of the end two n test files resided in a test folder. I configured ts configure to compile both these to look at just these two folders, and by default TSC tries to compile of the files to be next to the original files. So if you have let's say a inn x dot ts, it will generate an x dot js

alongside it. That is not what I wanted. The reason that this is not what I wanted is that my project is full of JS files and I want them to be processed as well, and I can't generate an x dot js file from an x dot JS file into the same folder I need. I have to generate it somewhere else. So I had to configure TSC to build everything into a disc folder, so it would take everything from the src and test folder and build them into a disc slash slash SRC and a disc slash slash sorry Test.

Speaker 1

Yes, So I don't know if I'm jumping ahead here, so pardon if I am. But is it normal practice to have like a TS file for the typescript and that compiles to JS or is that yeah?

Speaker 3

But I'm going to do Yeah, But like I said, I have one thousand JS files and I don't want to just arbitrarily rename all these files to dot TS. So from the way that I wanted to work is that is to manually transform each and every file at a time and do it gradually over time. So this project going all the way. If we ever go full TS, it might take years, and you know, but we'll, you know, every day or two will take a JS file and move it that particular file over to TS. But until

we do, that file stays as JS. So I really need to have the TSC compiler take all the TS files but also also all the JS files Effectively, I needed to quote unquote compile the JS files as well as the TS files because I need them all to be compiled into the same folder, because otherwise I'll break

the project structure. I can't just compile the TS files if I. If I have there a one thousand five files in this project that are JS and three files that I've moved over to TS, I need the resulting fold, the resulting this folder to have all the JS files, both from the originals and from the ones that I've already moved. I hope I'm explaining it clearly enough.

Speaker 2

Yeah, I get it.

Speaker 3

The other thing is I did is I enabled incremental build for speed. Incremental build means that that TSC maintains something like a cash and it sees that, hey, this file that I've previously compiled, nothing has changed in it, based let's say, on the time and date, so I

don't need to compile it again. And this way you don't need to compile the entire project each and every time, even though in our case it was very very quick, precisely because most of the files it was just taking JS files and generating JS files from them almost as is, which apparently it can do very quickly. They specified the latest possible target based in terms of the JavaScript support,

based on the no JS version that we're using. And since as I said, the project is using common JS, which means it uses a required rather than import, and also all the tests that we had did a lot of their mocking based on the assumption that require was being used, I can figure the output to be common JS. That means that in the ts files that we're creating, we can actually start using imports, but when the JavaScript,

when the TSC compiles it into JavaScript, those import become required. Now, again this is less than ideal because ideally we would like to also move eventually from common JS over to ESM. But you know, one thing at a time, you can't do everything all at once. So the next thing that I did was allow JS files, since as I explained multiple times, we aren't actually converting most of the files

yet most of the files remain JS. What it means by allowed JS files it means that the ts C will also process the JS files, effectively copying them over into the disc folder when it compiles the project.

Speaker 2

So that you had to do that.

Speaker 1

Could you mentioned that earlier about about creating that.

Speaker 3

Because I don't. If I don't do that, think what would happen That this folder will only contain the like the three JS files that were compiled out of the TS So sorry, I wouldn't be able to run the original because the original already contained some TS files and no doesn't know R to run them, at least without

that special command line switch. The disc folder will just contain the JS files generated from those TS files, so most of the files will be gone, so neither the original source nor the disc fold there would be runnable. I wanted the disk folder to be runnable, which means that all the JAS files needed to be compiled quote unquote into that folder as well.

Speaker 1

So my point was just that you had to have this in conjunction with the earlier build step that copies everything to the separate discfolder. You'd get a whole lot of nothing.

Speaker 3

Basically. Yes, now, some of the code, some of the Jazz files actually required JSON files that were also located in the src folder with him. So what you also need to enable is the flag that tells TSC to handle to resolve Jason files. So it sees that a certain JavaScript or typescript files requires a jason file. It will also copy that Jason file over to that disk

folder in the same relative location. And because if I initially neglected to do that, it didn't find those Jason f files when I try to run the project and that turned out to be bad. You can easily search your folder and see if you've got any files that don't match the dot js or dot jsx extension, and then you know that you need to make sure that these are copied over into the disc folder as well,

and preferably by the TSC compiler itself. The next thing that I needed to do was enable source maps for debugging, since I still want to be able to debug the project, but now I'm actually running it from the disk folder, but I want to debug it in the typescript files. I want to see the original code that I wrote when I put in break points, not the compiled output code. It's very similar, but it's not identical, and I prefer debugging the original. So in order to achieve that, you

enable source maps. Source maps are maps with the dot map extension that are generated by default in the same disk folder alongside the JS files. So if you had an x dot ts it gets compiled into the same relative location, and the disk folder into actually two files, one with the dot x dot JS and one with called x dot map. And actually at the very bottom of the JS file, it adds a special comment that tells dev tools where that where the map file is located.

And in this case it's easy because it's located in exactly the same place, so it's a simple relative location. Map files enable debuggers like dev tools, which is built into Chrome or Edge or effectively any dev tools of any debugger any browser, sorry or the built in the buggers in most IDs, to be able to single step through the original files because the debuggers is smart enough to understand that this line in this file is actually translated.

It matches that line in the original file, and here's the location of that original file relative to the compiled output file. The next thing I enabled was strict type checking. I was kind of concerned about that, but there was no problem with it, so I used that. And I also in our case, because we're using common JS, I made sure to enable the flag that instructs TSC to always put you strict at the type of at the

top of the generated files. If you're using ESM, you don't need it because ESM files arem module files are strict by default. But in our case sense again, since everything was compiled to common JS, I really wanted to have you strict there to get strict behavior.

Speaker 1

Can you elucidate on what strict means or what it's going to do.

Speaker 3

Yeah, Strict is a very old JavaScript feature, dates back to US six or before. Basically, there was certain let's call it less than ideal behaviors in JavaScript that they unfortunately could not remove out of the language due to backward compatibility issues, and the way that they decided to fix them was add this use strict directive that if you put it at the top of your file, for example, it's just a string quote open quote U strict close quote.

It puts the JavaScript engine in strict mode, which changed just certain those certain bad behaviors a couple of examples. The with statement turned out to be a bad idea in JavaScript for various reasons. With you strict with is gone. There's also a difference in what happens if you try to use this outside the context of an object. You know, if you just have a function and it does this dot x before you strict, it would default to the

global object with stricted defaults to undefined. So they they made certain changes that improved the behavior of JavaScript, but they were not backward compatible, so they had to add the TC thirty nine way back when had to add this flag in order to support it. Now in modern environment like EES models, if you're using ESM, then by by definition you're using e S six, then you you're they can assume that you're US strict by default because

there are no backward compatibility issues there. Likewise, if you're inside of a JavaScript class, again it's modern JavaScript by definition, so they can assume you strict within that class, regardless of whether you put ustrict that in the top of

the file or not. And the last thing that I needed to add was to skip lib lib tests, which checks that you have type definitions for all the LIBS libraries that you're using, because you were using some old and even almost deprecated libraries which and especially some internal libraries did not have type information, which means that did they did not have a d dot t S files. If you're familiar with the concept, yes, we.

Speaker 1

Would clarify that lib is not liberal versus conservative, that is library.

Speaker 3

Yeah, exactly. So you've got a library let's say let's say you have a library and it was compiled when it was built from typescript to JavaScript, and you are only distributing that library because it's it's an NPM package. You're only distributing it as the compiled output the JAS files. But you still want the type information, so that code that uses that library can can only call library APIs with your passing in the correct parameters of the correct

types and stuff like that. So what the Typescript compiler can do is it generates a file with a dot D dot ts extension, and that file contains type definitions for that library, so that Typescript knows to verify that you're using that library correctly. But some of our own libraries, especially internal libraries, were old, did not have this support, and unless I told it to skip the library tests, it would fail because they would say, hey, you're trying

to use this library. I'm looking for type information for this library and I can't find it. I can't use it, you know, abort. By setting that skip lip tests, I was able to tell TSC, you know, okay, it is what it is, just deal with it. Give the things that you import from that library type any effectively, well not effectively literally now, since all the JavaScript files are also processed as I explained. As I said, I needed to specify everything to go into a disk folder to

avoid collisions. And obviously you need to add this disc folder to your get ignored for example, because you don't want to save that disc folder into git up for example, because it's you know, you don't ever put in git auto generated files. The next thing I wanted to do is install types for various libraries that I use, So you basically NPM install or arn't add things like at

types slash node or at types slash Express. Basically there are a lot of these type these things that you can install or that are at types slash something based on the service or library that you use, and they just make sure to add the type information for those various libraries and services. So now that if I use a Node API, I get the type checking for that Node API, or likewise for Express, which is really great

and what we really want Typeescript for. Since I had to put Typescript itself as a prod dependency, I unfortunately had to put these as a proud dependency as well. In your case, if your CICD allows it. You can put both d S and these type these type packages as as dev dependency sorry, assuming again your CICD pipeline supports it. I also needed to install a package called source slash map slash support to get the proper debugging support in all the environments that I use with the

map files that have already configured. The generation of a lot of steps and again not trivial. I for some reason I couldn't really find Maybe our listeners are better at they have a stronger Google for AI food than I have and can find this information. I could not find a clear description of all this information. The next step that I had to do is I had to do with eslint. So I assume or hope that you're using islint in your project. If you do. If you don't,

then you should. We had guests on our show talking about eslinteslint is a linting tool that verifies that you're using the language JavaScript according to best practices you know, either generally accepted or ones that you've decided on within your organization. It could be almost comical rules like you know, prefer quote single quote over double quotes, so you don't have a random mixture of both types of quote in

your project. Or it can be about things like putting the curly brackets at the end of the line rather at the beginning of the next line, and so on and so forth, everything that you prefer in terms of coding style and avoiding legal but bad patterns within your code. Now, if you're using Typescript, you also want to install something called t s slint or Typescript e slint. We had Josh Goldberg on our show to talk about why you want this. Basically, it's es slint for your Typescript. Now.

You need it for two reasons. The obvious reasons is that it adds particular specific linting rules for Typescript itself. I mean obviously because similarly to JavaScript, there can be things that are legal in Typescript, but you nonetheless prefer to avoid because there are sort of anti patterns in the language. But it actually goes beyond that. It turns out without t S slint, eslint itself will flag certain things in your Typeescript code as bugs because they don't

match lint JavaScript linting rules. For example, you can use extends in JavaScript in TS in Typescript generics, but it assumes that extends its used for extending JavaScript classes and that you're using it wrong and it flags it. So you need t S e S Flint for the linting to work properly. Now, if you're adding t S slint, you need to make sure that you're using the latest version of eslint. Unfortunately, because we were a legacy project in our case, this was not the case. So I

had to update eslint to the latest version. And then I found out the hard way that they the Slint project, made a significant, not backward compatible change in the way in which they managed configuration. They went to a flat configuration file. They basically changed the whole structure of the configuration.

So I had to go through the process of fixing the configurations and as long as I was there, rethinking the configurations because a lot of the rules were dated, so I updated our Slint rules as a part of this migration process. As you can see a lot of work.

The next issue that I ran into was the fact that some people on our team prefer using vis Code and some people in our team preferred using web Storm for JavaScript development, and I wanted to support both I didn't want to force people to have to switch their development environment, which means that I had to get at everything working in both. And where this ran into a

problem was with the unit tests. Web Storm out of the box had this ability, has this great ability that it can actually scan your files, find the unit tests, and make it possible to run individual unit tests and debug individual unit tests directly from within the web Storm environment. You can actually do the same thing in VS code, but you actually need to install an appropriate extension first.

Once you install the extension, it's great. I actually prefer vis code with the extension of a WebStorm, but that's me.

We had developers who preferred using WebStorm, and for the life of me, I could not get WebStorm to handle the situation in which the TS file files were located in one place and the output JS files were located in a different place in the disfolder and to figure out that even though the test code the source code for the test was here, it should actually run the code that's over there and use the map files to properly support the single step and debugging with the VS code.

And as I said, we were using Mocha and Shi on VS code. I was able to install those the vs code extensions for Moca properly configure it. It just worked. In that scenario WebStorm, I could not get it to work. The only way in which I finally was able to get it to work. As I mentioned before, this tsnode thing where it's a wrapper to node that teaches node how to handle TS files. So I actually had to configure the template file for Moca on WebStorm to use

tsnde instead of nodes, and then it worked. So I used a different typical configuration between web Storm and between vs code and vis code. I would I preferred the test to run closer to the way that they run in the CICD pipeline. That means to run the test and the compiled tests and the disfolder. And as I said, I could get I could get it to work with the source maps because the extension from Mocha was smart enough to handle it. In the case of WebStorm, I

don't know if it can or it can't. I could not figure it out. I had to use ts node.

Speaker 2

Now you can still run the test from the command line, Is that right?

Speaker 3

Yeah?

Speaker 1

You're talking about is like the UI you know, it gives you a little button you can press run this test.

Speaker 3

Exactly exactly which you want if you want to debug tests, because when you have that button, you can actually right click it select debug instead of run, and then you actually single step that test within your ID, which you know, if you have a failing test, the ability to debug that failing test makes life a whole lot easier. It's certainly much more, much more fun than console log debugging exactly.

And the other thing I needed to do was configure the Mocha itself to run all the tests from this test instead of tests. One more thing, and now I had to start modifying scripts in the package json itself. You know, we had all the built scripts and run scripts and dev scripts, so I added the script for example, a built script so that you could run TC as NPM run build or Yarn build would literally just run TSC. But I also wanted to support a more dynamic mode

for development. You know, in development, you want for example, node, Node has this ability to watch your folder, see that it changes, and then restart itself whenever it identifies that the file is changed. But now it becomes a more complicated scenario because what actually needs to happen is you save that file. You want that file. You want ESC to notice that the file is changed and then compile it to the disk folder. And then Node you want it to notice that the contents of the disk folder

is changed and to restart itself. So it becomes like a two step instead of a one step. Is that clear, crystal? So initially I did the naive thing. There was dev script in the package Jason that literally had uh node running Node in watch mode. I forget the syntax. Let me take a quick look at how it actually is implemented. So it had it had it used nodemon with a watch to actually automatically reload Node whenever the content of the folder change. And originally it was running things directly

from the source folder. I wanted noemond to look at the disc folder instead. That was easy enough, but I also wanted uh TSC to look at to watch the source folder and see if it changed, to compile automatically into the death folder. And there is a TSC minus minus watch that you can use for that. So originally, initially, so one thing you can do just run those two commands separately. You can open like two terminals, or you can use in vs code there's this thing that they call,

I forget the name tasks you can do. You can run both two tasks, one to have TSC watching your source and node more watching your disc. But I did want to have to do to force our developers to do these two steps, where in the past they just did one. In the past, they would just do yarn dev and it just worked, and I wanted to work this way. So initially I was kind of naive and I did TSC minus minus watch, then hun percent unpercent node mon minus minus watch, and it didn't work. Can

you guess why it didn't work. It didn't work because un percent unpercent waits for the process on the left to finish successfully before running the process on the right. If you remember your Bash.

Speaker 2

Oh sorry, I'm not a Bash guru, but hey, strip, so.

Speaker 3

It was waiting on TSC minus minus watch to successfully finish before running a nodemond. But TSC minus minus watch never finishes. That's the whole point. It runs continuously in order to watch for changes in the source file never

so it never actually ran the nodemd Oh. Yeah. So the easiest solution that I finally found for this is another package called concurrently, which you can tell it to run both things simultaneously and kill them both if you do a control C. So look at a package called concurrently. That's what I used to fix it. Maybe there's a smart maybe smarter Bash gurus can do it without it. That was the easiest solution for me. That's what I

ended up using. Notice one important thing that I have not done so far at all, and that's kind of important. I wanted to emphasize this. All this work was done before I converted even one JS file to Typescript. So I've got all this pipeline working and TSC working and whatnot, but it's basically compiling JavaScript files to JavaScript files. I don't have a single TS file in the project yet.

And that's really important because initially I kind of when I kind of got ahead of myself and I started moving JS files to TS in order to see that things worked before I had everything properly working, and then I ran into all sorts of problems, which eventually ended up with me throwing the whole thing away and starting from scratch. So make sure you've got everything working so far before moving even a single JS file to TS,

because otherwise you'll be sorry. So create a PR from everything that you've done so far, make sure that everything according to what I explained. Make sure that your CI, your updated CICD now passes while running TSC again without converting any TS file yet at all.

Speaker 1

And I didn't with the assumption being that when you do convert TS files and everything's in place, right, you haven't. Everything you've done is to handle the conversion of the typescript files to jobascript exactly.

Speaker 3

I wanted to get to a situation where everything else worked and then I can start converting files individually and not have to worry about it. I didn't want to deal both with the configurations and the conversions at the same time. So I created a PR. I verified that the whole CI process passed for that PR. I didn't merge that PR. I just wanted to see that it

was green. Yes, after I saw. After I saw that it was green, I moved one and just one file from being a jazz file to a TS file and to see that it still worked.

Speaker 2

And by that you just changed the extension.

Speaker 3

Right, No, because that's not enough. If I changed the extension, it would complain about various missing type declarations and whatnot. I can obviously set it to super relaxed, not the strict mode, but I intentionally wanted strict mode. So I actually I chose a really small JS file, literally one of the smallest files in the project that I can found, one that was not dependent on any other project, a

file in my project. So it was literally a leaf file only it may depend on external packages, but not on anything inside my own project. And I just converted, but I made sure that that jazz file was actually a file that's being used. I didn't want to convert something that nob the actually imports or requires, uh, And I converted just that JS file D two ts. I did it using get m vy or get move in order to preserve its history. Are you familiar with Git move?

Speaker 2

Oh, yeah, very much.

Speaker 1

So it's basically it's it's one of the weird things about get, At least to me, it seems a little weird. Maybe it makes sense when you think about it. If you want to rename a file, it's not a renamed command. It's a move command to move it from one file to another.

Speaker 2

Yeah, kind of the same making a branch, same thing with the branch exactly.

Speaker 3

So I did a geit move of that a single file from JS to TS, preserving its history. I added the type information. I made sure that that it's a file that's actually as I said. Then give the rules. All file as small a file as I can find, not dependent on any the other file within my own within that particular project, but can be dependent on external things.

Is used by one of the unit tests again either directly or indirectly, but small and simple, and is required by somebody again by a by a unit test or by and buy some code and and converted that. Added the type information. UH, make it as simple as possible, avoid changing execution logic, business logic. Just add the type information, committed it, committed that change. UH, created a commit for that change, updated the PR and saw that it still worked. And happily you did so.

Speaker 1

You mentioned you said when I UH mentioned about just changing the file from JAS or even moving it from JST, that.

Speaker 2

It would still fail.

Speaker 1

So in a typescript file, just for clarity, is there something else that you have to put in there that says no, you.

Speaker 3

Need to put it in the type information and unless you make as I said, as I mentioned when I created the TS CONFIGU Todd jason file. I set it to strict type checking. I could have not put it a strict type checking, and then I could have just renamed that file. But I put it a strict which means that it expects to see type information on things like variables. Unless it can in further type information, it needs you to give it the type information. So if you do a const x equals three, it knows that

x is a number. But if it's just let x without signing anything, it can't know the type of X, So you need to be explicit about it. Likewise, if you've got arguments, you need to tell it type of the arguments. It can in further return value, but you need to give it the types of the of the function parameters or function argument. So when I renamed the TF file from JS tots, I also needed to put

in the relevant type information. After all, that's the whole point of moving from JS tots, and I wanted to see it handle that type information. And as I said,

I moved just that one file. I pushed it in and verified that everything that all the build works, that the unit tests work, that the end to end test work, that deployment is created, that I can actually use that created deployment, and I even looked in the disk folder to verify that the JS file was created for that TS file, and then it contained what I expected it to contain. So it was that that anal about it, really, just because again it was that first file. After that

you can be more relaxed than your attitude. I then intentionally introduced a typescript error into that file to see that it failed. Because if you don't see that everything fails, how do you know that it actually even working and not just doing something stupid. So I put in intentionally a typescript error. Shane, let's say something supposed to be

a string. I said that it's a number, and saw that typescript you know, failed and the build failed, And then I fixed it again and saw that the build passed again, and I and I and as I said, I pushed it into that branch and saw that the CI process failed, passed, sorry, and everything was green. But again I didn't merge it yet. Instead, I deployed it to our testing environment, and I made sure that I could properly debug it both using vis code and web Storm.

We've got a rather complex complicated for various technical reason environment for debugging, which involves remote deployments and stuff like that. It's not really important in the context of this discussion. I won't go into the details. I then asked some of my colleagues to test out that branch as well, see that they're also able to use it and debug it and that everything just works for them. Again, I

was very very careful about it. After I got the okay from those colleagues, I rebased the branch, and because we use rebase rather than merge, because it took me a while, so obviously other changes were done in the project during that time. So I rebased and finally merge that pr and finally we had a version that supported Typescript and had one and exactly one TS file in

that project out of those one thousand JavaScript files. And I also made sure not to have to do that right before the weekend, so that I don't break everything just before the weekend.

Speaker 1

Well that's the general rule anyway, right, you don't do major deployments on Friday.

Speaker 3

After merge, I verified that the main branch again was also built correctly, that all the end to end test paths, that everything worked correctly, I was ready to roll back otherwise. Fortunately I did not have to. I then basically informed everybody else about this change, and we started discussing about how we can gradually move from JS to TS. And we've been doing it very slowly since then, and that more or less concludes where we are now. And it was, as I said, it was a lot less trivial than

I expected it to be. Now some of the issues had to do with our own unique environment. But still now I have heard other suggestions about how to go about it. For example, convert your repo into a mono repo type environment, start pulling out self contained files as separate packages within that mono repo, and since there are new packages, just make them TS from the get go. You can do that. You still need to be very careful about how you preserve history and stuff like that.

It's not trivial, but that's not what I did in this project. Anyway, I might actually have to do that other way in that project with JavaScript and flow, because I don't take I can get both JavaScript and flow to work within the same package. Anyway, I'm done.

Speaker 1

So, how many files have you actually now that all that's in place, how many files that you actually.

Speaker 3

Converted not so many so far, approximately ten out of the one thousand. This will take time. The biggest problem in converting files is that, well, you know, sometimes you run into complicated types that require generics and stuff like that, and you need to get the team up to speed about typescript because a lot of them have been basically just using javascripts, so they're not as familiar with typescript as ideally they should be, and you need to allocate

time for that as well. But the bigger issue is that the move to typescript has actually uncovered various bugs in the code, places where null tests were missing. So one of the great things about typescript is the typeescript basically having is explicit about something being that can be null. So it's something is can be either if something is, then if a variable is of type number, it cannot be null. It you basically, if you wanted to also be null, you need to explicit be explicit about and

say number or null. And it uncovered a lot of places in the code that null tests were not properly put in place, or places where library third party libraries were used and a version was updated that changed the API, but the way in which that library was used, wasn't updated, and the wrong parameters were actually passed into certain APIs. Now somehow it worked, but now Typescript complains about it. Then you have to fix it, and you have to

think about, you know, how do I fix it? It actually requires changing the actual execution code in order to get Typescript to be happy about it. If you're passing something which is a number into something that should be a string and it happens to work, you know, you don't just want to cast a number a string. You want to fix it. You want it to work properly. So the biggest time consumer really so far has been basically fixing bugs that Typescript uncovers.

Speaker 1

Yeah, I've been so I do a lot of work leral on PHP in my day to day, and so the past couple of weeks I've been working with PHP STAN, which is a PHP static analysis tool, hence the name STAN.

Speaker 2

And STAN. I definitely have a love a lot of hate relationship with it. Well.

Speaker 1

A couple of things that I found out is one cases you're correct, it does the similar thing where you have to declare okay, it can be the this or no. You know, I have to clear the options well, a lot of times, what I find myself having to do, and I'm curious to see if you've run into this as well, is having to restructure working code for the tool.

So in other words, if if you have a step that has that you know, does some chaining and does multiple things, sometimes you have to break that apart so the static analysis tool can realize, okay, this variable is of this class and then you can go on into it where it works fine, you know with with when things are chained together, but you have to break things

apart strictly for the static analysis tool. That was probably one of the bigger frustrations that I've you know, in dealing with that, that I've had to fight.

Speaker 3

Look you can, yeah, but I'm thinking about how to phrase it. First of all, I'm not familiar with PHP stand but I can say that typescript is amazing in the in the sophistication of the static analysis that it can perform. So, for example, if something is if a variable, as I said, is number or null, and then you do an if in the code and check that it's not null, then it knows that within the scope of that if the type of that variable is now assumed to be number and not number or null. It's really

smart about how to infer the correct type. So the value that the type of of of a variable actually changes according to the code because of various type checks that can exist in the in the runtime code. These are known as type guards. We actually talked about in the in the recent episode that we did with the Lyran and Ariel. You were in that episode, I believe, right, yes, yeah, so we actually talked about that. So Typescript is amazingly

sophisticated about such things. The fact that you need to break the code into smaller pieces. From my perspective, that's actually a benefit. I like my code broken down in smaller pieces.

Speaker 1

Right, and I tend to be that way too. You don't want to, you know, have fifteen chain functions on there, just for the sake of having shorter code. If a little more broken up code is easier to read. But when it's already working, you got to break it up just for the tool, it can be frustrating.

Speaker 3

I agree with that, and that exactly goes to the point that I mentioned that the biggest challenge so far has been those situations in which we actually had to change the JavaScript in order for Typescript to be happy about it, and in most cases the change was for the better. It was a good change, but it's still work. And once you know ideally, you're thinking, I'm just adding types, so all the tests should pass because the types are removed from the generated code anyway, so they don't impact

the tests. But once you have to start modifying the code, you also start breaking tests, which is another reason why you should have good test coverage when starting projects like that.

Speaker 2

Right, sure, Okay, I think.

Speaker 3

We're more or less run out of time.

Speaker 2

Yes, I believe so. Quick question.

Speaker 1

You had mentioned at the beginning that you had documented this on your internal wiki, and we've got this episode obviously, but are you going to put this out in written format publicly, like a blog post or something.

Speaker 3

I probably should.

Speaker 2

I seem like some pretty valuable information.

Speaker 3

I think so too. As I said, maybe it exists somewhere, but I just couldn't find it, but it probably is. Probably should. The only problem is that I I don't really have a blog anymore, so I need to figure out about where to actually put it. I'm also, as I mentioned, I think I may want to submit it as a conference talk, not that you know it prevents I can do all three. You know, we now have a podcast. I can have it as a blog and as a proposed conference talk. I just need to figure

out where to put that blog post. Probably should You're.

Speaker 2

Right, all righty, So that will move on to picks.

Speaker 1

Picks are the part of the show where we can talk about anything else, or we can talk about tech if we want. I'll give Dan throw to break since he's been doing most of the talking here, and do my dad jokes of the week. Maybe we'll get a laugh from Damn. Maybe not so recently. You know, I had to change a password, and I normally use a password manager, but this particular time, we'll say this was

back before password managers. I need a password that had eight characters, so I did snow White and the Seven Dwarfs because seven plus one is eight. So I've got three kids, and when our last one was born, my wife was in labor and I tried to tell her jokes to distract her from the pain, but she didn't last at all. It must have been the delivery, right man. Yeah, this is this is a recent one. I love it because it's somebody pointed out to me on Twitter it's

both funny and inspirational. Says when you think that all is lost, there's no hope left. Just remember the lobsters in the tank in the Titanic's restaurant.

Speaker 3

Oh that's that's a good point.

Speaker 2

I'm free, I'm free. Anyway, Those are the dad jokes of the week. Dan, you got any picks for us?

Speaker 3

Yeah, not that many, but a few. So one pick I have is a friend of mine here in Israel called the lead Josef great confidence speaker by the way, So he created a very interesting project. There's he actually put on X the whole story, a thread about the whole story about how they built this thing. It's an incredible read. Unfortunately it's an Hebrew I know that it has a built in transition capability, but still but anyway,

this this really interesting thing is called get mcph. I spoke about MCP with Jack Hankton when we had him as a guest on the show. MCP I forget what it's what it stands for, but it's basically a standard protocol for connecting stuff to llms. So the llms can actually use that protocol to get additional context, additional information,

or to control various external resources. So they created something called get MCP, which basically makes it possible to instantly create an MCP server from any getub project and then you can hand it over to something like Cursor and immediately Cursor has context based on that getub project, which is really interesting. So if you want to learn about some getub you know, you can obviously clone a getub project and open it within you within Cursor and then

you get context. But suppose you want to you're working on your own project that uses some other project APIs from some other project, and you want Cursor to be able to help you generate code for using those APIs. Well, with get MCP you can just point. It's literally just replacing the word GitHub in the URL with geitub dot com with get mcp dot io. That's literally it. That URL will immediately work. You just hand over that URL to Cursor, for example, and then and instantly Cursor has

context about that project. It seems like magic to me. I've not tried it myself yet, but it literally seems like magic. Some of the videos that they have on their website literally seem like magic. It's it's pretty amazing the stuff that you can do. These days with these tools.

Speaker 1

So MCP stands for you mentioned the CEE. It's a model context protocol.

Speaker 3

Right, it's a this protocol proposal that's supposed to be a new standard for connecting llms to stuff, and uh, it's pretty amazing what they've achieved. So it works with Claude, it works with Cursor, it works with Windsurf, it works with vis code. As I said, I've not tried it yet.

I've only learned about it effectively yesterday and I haven't had a chance to play with it, but it seems pretty amazing the fact that you can literally just by changing replacing the word in the URL from getub to get MCP and immediately get it to work, it's just amazing to me. And the other pick is I think I mentioned that I'm speaking at a conference in Romania

towards the end of May. It's called JS Heroes. I'll be talking about how modern web frameworks use or prefer using RPC as the communication protocol between the front end and their back ends. If you're thinking about frameworks like next JS or like quick or like solid, they're all or ten stack. They're they're kind of moving over to using our PC, So I'm talking about what RPC is and why they're doing it, and what are the implications,

the good sides the bad sides of this transition. And the thing is that, first of all, I have to say that Jas Heroes Conference, the agenda looks amazing, The speaker lineup is really awesome. The interesting thing, though, is one of the organizers is Tagus. We've had Tags on their show a few times. Tags is an amazing person, and he kind of tweeted recently that a lot of conferences have effectively disappeared in the past year or two,

that there are very few conferences actually still running. And now he should know because he's very involved in the in the conference scene. He does like thirty conference talks a year or maybe even more. He's really into it. So if he's saying that a lot of conferences are shutting down, I trust him about it, and it sounds really unfortunate to me. My information myself is anecdotal. I'm looking at services like sessionize, and it does seem to be the case that there are a lot fewer conferences

out there. That's really unfortunate, and it seems to me that we should be supporting the conferences that are still running by participating by buying tickets, and attending by sponsoring. So if you can help a conference organizer keep the conference up and running, please do my last pick since I mentioned Tagous, Tajos has his own podcast these days. It's called Contagious Code, which is a nice play on words.

Speaker 2

I was going to say, yeah, that's a good name right there.

Speaker 3

And he recently posted a new episode which is really interesting. Usually most of his episodes are about he interviews people you know in a same similar vein to the way that we do. And and you know, even though we have a podcast, he has a podcast. I have no problems saying that his podcast is awesome. I highly recommend listening to it. In fact, I was a guest on his podcast a while back. But the reason that I'm mentioning it is that his latest episode, that he basically

put out a week ago, is not an interview. Rather, it stages himself speaking for one and a half hours about the state of AI in twenty twenty five. He also, you know we mentioned MCP. He talks about MCP in detail. He talks about agents, he talks about RAG and much more. And I'm about halfway in. It's excellent. I highly recommend listening to that episode. I hope we can get them on our show as well, but regardless, I recommend listening to that episode to get really up to speed about

the technicalities of AI in twenty twenty five. And those will be my picks for today, all.

Speaker 2

Right, So with that we will wrap things up.

Speaker 1

Hopefully this provided good information from you that for you that are going to be converting code basis to typescript and Dan, I can people follow you on Twitter if they want to get info or do your Twitter or where can they find out if you publish this written form somewhere?

Speaker 3

Well if if and when I do, I'll obviously announce it on X, so yeah they can, and if they, if they're on X, please do follow me. I'm Dan Shapier. That's d A N S H A P P I R on X. I'm also on Blue Sky if you prefer that platform instead, so I'm there as well. On Blue Sky, I'm also Dan Shapiir, so find me there as well. Follow me here or there or anywhere. I'll gladly follow you back. Also glad to get feedback about our podcast in general, So feel free to contact me and that's.

Speaker 1

It, alrighty, And if you want to get a dad joke today, if you're amazed the quality of my curated dad jokes, you can follow me on x slash Twitter at Wonder nine to five. All right, with that, we will wrap this poppy up, thanks for listening, and we'll talk it to you next time.

Speaker 3

Bye,

Transcript source: Provided by creator in RSS feed: download file
For the best experience, listen in Metacast app for iOS or Android