Speaker 1: Hello, everybody, Welcome to another thrilling episode. I was going
Speaker 1: to say, example episode of JavaScript. Jower, I am Steve Edwards,
Speaker 1: the host with the face for radio and the voice
Speaker 1: for being a mine, but Dan got to me the host,
Speaker 1: so you're stuck with me speaking of panelists. Was I
Speaker 1: speaking of panelists?
Speaker 2: Now I am?
Speaker 1: Anyway, Dan Shapiro is here with me. We are flying
Speaker 1: 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
Speaker 3: a winter this year, to be honest, it was more
Speaker 3: like a bit of a fall. Not that you know,
Speaker 3: we should have more rain than we did. But you know,
Speaker 3: it's still lovely there at least that and it still
Speaker 3: 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
Speaker 1: about a practical application of pipescript in JavaScript. I'll let
Speaker 1: Dan give the details, but we're going to talk about
Speaker 1: everything that was involved in Was it a new project
Speaker 1: I already forgot. Oh so yeah, I'll let you talk
Speaker 1: from here so I don't mess it up anymore.
Speaker 3: All good. So basically I had this interesting situation at work.
Speaker 3: We have a large legacy JavaScript project that's still a
Speaker 3: core part of our offering, and it's under active well
Speaker 3: beyond maintenance. It's being actively developed and enhanced and improved
Speaker 3: and whatnot. But it's all done in JavaScript on hundred
Speaker 3: percent JavaScript because it was started more than eight years
Speaker 3: ago and it was never moved over to typescript. It
Speaker 3: just stayed JavaScript, and I was looking at introducing typescript
Speaker 3: into that project. Actually that project has two parts. It
Speaker 3: has a server that's just running on no JS with Express,
Speaker 3: and it has a client or front end side, which
Speaker 3: is implemented in React. That part actually again talking legacy.
Speaker 3: Some years ago before I joined the company, they made
Speaker 3: the kind of unfortunate choice to introduce flow into the
Speaker 3: front end part for type safety, and they only did
Speaker 3: it partially. I didn't want to touch flow yet, so
Speaker 3: I was looking at introducing type script to the back
Speaker 3: end part, to the no JS part of the project,
Speaker 3: and it turned out that it was much more complicated
Speaker 3: and error prone than I expected. There were a lot
Speaker 3: of pitfalls and gutchas along the way. I kind of
Speaker 3: thought that, you know, given that the whole idea of
Speaker 3: typescript is to get JavaScript devs onto the bandwagon, that
Speaker 3: it should be really easy and straightforward, and it wasn't.
Speaker 3: And I even consulted with some typescript luminaries like Matt
Speaker 3: Polcock had a lot of great advice and suggestions, but
Speaker 3: it still wasn't easy. Also with Jack Harrington and others,
Speaker 3: they all had great advice, but it still was much
Speaker 3: more challenging than I expected. And the end result is
Speaker 3: that I kind of documented the entire process, did a
Speaker 3: bit something like an internal blog about it, and we
Speaker 3: have this internal wiki. So I wrote the whole doc
Speaker 3: about how to do it so that if and when
Speaker 3: we do it in another project, it'll be much easier
Speaker 3: that time around. And I thought that it would be
Speaker 3: interesting and useful for our audience for me to show
Speaker 3: this information. I hope that it is okay.
Speaker 1: So just to clarify, we're on the front end, you're
Speaker 1: still sticking with what was there, right, In other words,
Speaker 1: you're going to go with the flow, should we say.
Speaker 1: And then on the back end was where you're doing
Speaker 1: your typescript upgrading.
Speaker 3: Yeah, So the answer is actually that our ultimate intention
Speaker 3: is to do typescript on both sides, but I wanted
Speaker 3: to start somewhere, and it seemed easier to start with
Speaker 3: the just JavaScript part because Flow and typescript, while they
Speaker 3: are similar, they're not the same. They're not compatible, and
Speaker 3: I didn't want to get into the whole mess of
Speaker 3: how I get typescript and Flow working together in the
Speaker 3: 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,
Speaker 3: let's put it this way. I really wanted to do
Speaker 3: everything gradually. It's important to emphasize this point. So for example,
Speaker 3: talking about just to give an an understanding of the
Speaker 3: size the scope of the project, So talking just about
Speaker 3: the back end part that no JS project, we're talking
Speaker 3: about close to one thousand files JavaScript files, about seventy
Speaker 3: thousand lines of code, something like thirty five hundred commits
Speaker 3: since the project started over seven close to eight years ago.
Speaker 3: So it's not a small project. And obviously I can't
Speaker 3: move all the JavaScript files over to typescript, all thousand
Speaker 3: of all one thousand of them in one go. Now,
Speaker 3: there are some automated tools that automatically move all your
Speaker 3: JS files into TS and add types as best they can,
Speaker 3: using all sorts of tricks and static analysis and whatnot.
Speaker 3: I did not want to do that. I was looking.
Speaker 3: The way that I wanted to go about it, and
Speaker 3: especially after I spoke with some people, is to enable
Speaker 3: a gradual migration. And I'll talk about the two ways
Speaker 3: in which a gradual migration can be achieved. By the way,
Speaker 3: if you want to understand why I'm looking at a
Speaker 3: gradual migration, why I prefer this approach, you can also
Speaker 3: listen to the interview that I did with Matt Pocock
Speaker 3: a while back about typescript, and we also discussed this
Speaker 3: aspect about migrating projects, although again we did not go
Speaker 3: into the exact details that I'm intending to go into today.
Speaker 3: So the idea was to basically introduce typescript into the
Speaker 3: project so that we can do the actual migration of
Speaker 3: the files slowly over time. It could literally take years.
Speaker 3: Is that is that clear? I hope? Yes.
Speaker 1: Sorry, I was looking at the Matt Pocock episode so
Speaker 1: I could tell people what it was.
Speaker 3: Yeah, we can probably put a link in the show notes. Yeah. Yeah,
Speaker 3: it was an excellent episode, highly recommended. Wait a minute,
Speaker 3: we did two episode with them. Are you looking at
Speaker 3: the latest one, the one from like about a few
Speaker 3: 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
Speaker 3: 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
Speaker 3: product built on top of NOGS with Express. Like AJ
Speaker 3: likes it, it uses common JS rather than import, so
Speaker 3: it requires it's a legacy project for an API with
Speaker 3: the front end side, it actually uses a combination of
Speaker 3: Swagger and open Api for public RESTful APIs and actually
Speaker 3: graphql with Apolo for the internal private APIs. For unit
Speaker 3: testing we used Moca and Chai and it has end
Speaker 3: to end tests implemented mostly using Playwright, but that's less relevant.
Speaker 3: As I said, almost one thousand JavaScript files, almost seventy
Speaker 3: thousand lines of code, and around thirty five hundred commits
Speaker 3: since the project started. And again we use squash, so
Speaker 3: it's really thirty five hundred prs really rather than you know,
Speaker 3: along the way the world a lot more commits, but
Speaker 3: they were just squashed together.
Speaker 1: By squash you mean, not the vegetable. But when you
Speaker 1: do the get merging in, you squash multiple commits and
Speaker 1: do one bigger commit exactly.
Speaker 3: Just a clarify for yeah people, because you know, there
Speaker 3: are a lot of small commits along the way. I'm
Speaker 3: fixing this. I'm fixing that they're they're not that interesting
Speaker 3: in most cases. You know, you can keep them if
Speaker 3: they're really important, but in most cases they're not that interesting. Okay,
Speaker 3: So I was looking to enable a gradual migration, which
Speaker 3: means introducing Typescript into the project, but not actually moving
Speaker 3: the entire project over to typescript. Maybe a few words
Speaker 3: about why I wanted to do that. It's really funny.
Speaker 3: I also spoke about this with Matt. When typescript first
Speaker 3: was introduced, I had a kind of a negative attitude,
Speaker 3: you know, when I came to JavaScript. Statically typed languages
Speaker 3: like C plus plus and like Java, like C Sharp
Speaker 3: and JavaScript. The dynamic nature of JavaScript afforded an amazing
Speaker 3: amount of freedom, and I loved it. But as I
Speaker 3: became ever more seasoned in JavaScript development, especially working in
Speaker 3: larger teams, I came to realize and understand that for
Speaker 3: larger projects involving larger teams, for production grade software, you
Speaker 3: want something that's more structured and rigid, and you want
Speaker 3: something that helps you avoid certain errors that can easily
Speaker 3: happen if you're just using JavaScript. You know, the obvious
Speaker 3: example is you just misspelled or mistype a property name
Speaker 3: and you just get undefined instead of the actual value.
Speaker 3: But there's no error in any other in you know,
Speaker 3: in any other way, and it seems to run. It
Speaker 3: just runs wrong, And that's one of those things that
Speaker 3: typescript protects you from. Typescript. Also, the code is much better,
Speaker 3: much more self documented, especially if you're talking about APIs,
Speaker 3: Especially again if you're talking about larger teams that compose
Speaker 3: software from parts, components or services together. You want APIs
Speaker 3: that are much better structured and self documented. And the
Speaker 3: integration with modern development tools like vis code especially that
Speaker 3: does the much smarter autocomplete for you and puts squigglies
Speaker 3: if you pass in wrong parameter types or the wrong
Speaker 3: number of parameters or whatever. All these are super useful
Speaker 3: tools when you're working, especially as I said, in an
Speaker 3: enterprise type environment. And these days it's also better with
Speaker 3: AI because it gives the AI greater context. By the way,
Speaker 3: all that being said, if I need to solve a
Speaker 3: lit code type problem, I'll probably just do it in
Speaker 3: JavaScript and not mess about with the types. But for
Speaker 3: everything else, I find that even though we are writing
Speaker 3: those types that ultimately are evaporated out of the code,
Speaker 3: because when Typescript is compiled to JavaScript, it basically just
Speaker 3: strips out all the types. Despite that fact, I still
Speaker 3: find it incredibly useful maintenance for the development and maintenance
Speaker 3: of larger certainly of larger code bases, and especially in
Speaker 3: this case, the fact that the project was done in
Speaker 3: JavaScript and not Typescript was definitely hurting our developer experience.
Speaker 3: Just recently, somebody literally introduced a bug that could not
Speaker 3: have happened had the project been entirely done in Typescript,
Speaker 3: and it didn't propagate all the way to production, but
Speaker 3: it was only caught by an end to end test,
Speaker 3: you know, later down the line, and it was much
Speaker 3: more difficult and expensive to handle than if vis code
Speaker 3: 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
Speaker 3: lot of front end developers or full stack developers still
Speaker 3: preferred to work in JavaScript. You know, you do you
Speaker 3: whatever works best for you. For me, after all these years,
Speaker 3: I've come to the conclusion that again, for the larger
Speaker 3: projects involving multiple developers, certainly I prefer you working in typescript. Okay,
Speaker 3: So I have the project, it's pulled down to my computer,
Speaker 3: and I want to start. The first thing that I
Speaker 3: went about, again because it's a legacy project, is I
Speaker 3: want to upgrade, to make sure that node is upgraded
Speaker 3: to the latest possible version, ideally to the latest LTE
Speaker 3: version LTLS version sorry, long term support version. And if
Speaker 3: people are curious about what that means. We had that
Speaker 3: conversation with the node guys about the nine pillars of note.
Speaker 3: Hopefully you're using something like NVM, the node version management,
Speaker 3: so you just you know, find the latest version you
Speaker 3: support you can support idea, the latest LTS version and
Speaker 3: put it in your NVMRC file. Update to that and
Speaker 3: make sure that that it runs and that you can
Speaker 3: work with it. Also make sure that obviously that you
Speaker 3: were pulled on the latest version from you know, GitHub
Speaker 3: or whatever, and that it appears to work properly in
Speaker 3: your environment. The next step is to make sure it
Speaker 3: seems obvious and explain why it isn't that your unit
Speaker 3: tests work correctly. Now you might say, well, obviously, because
Speaker 3: otherwise the project will be broken. But in our case,
Speaker 3: at least, it turned out that some of the unit
Speaker 3: tests had cross dependencies. What that means is that they
Speaker 3: used mocks but didn't always properly cleaned the mocks up
Speaker 3: the results. Some of the tests actually only passed when
Speaker 3: they were run in a particular order.
Speaker 1: No, yeah, I've ran into that before. I know exactly
Speaker 1: what you're talking about.
Speaker 3: Yeah, And it turns out that everything seemed to be
Speaker 3: fine because when it was run in the JavaScript project,
Speaker 3: it was it happened to run in the correct order.
Speaker 3: Otherwise people would have noticed and fixed it a while ago.
Speaker 3: But when I actually started moving the project over the typescript,
Speaker 3: it turned out that because of the transpilation step, things
Speaker 3: didn't run in exactly the same order, and then certain
Speaker 3: tests broke. Or I had certain situations where I needed
Speaker 3: to touch the business logic, and then I wanted to
Speaker 3: run particular tests individually, and I found out that when
Speaker 3: I tried to run the test individually, they failed, and
Speaker 3: then I went back to the original code and founded
Speaker 3: those tests. Even when I ran them individually in the
Speaker 3: actual original JavaScript project, they also failed because they only
Speaker 3: passed when they were run in a batch in the
Speaker 3: appropriate order, because again of mock leaks. I won't go
Speaker 3: into the detail of what mock leaks have are. Basically,
Speaker 3: it just means that a mock was put in but
Speaker 3: wasn't properly removed at the end of the test. It
Speaker 3: consequently leaked into the next test, uh, and it impacted
Speaker 3: that and in that case it actually enabled it to
Speaker 3: complete successfully. And when you just run that test on
Speaker 3: its own, it doesn't get those mocks, and then it failed.
Speaker 1: So in all the idea, I mean, obviously, I think
Speaker 1: the idea there is that tests should be self but
Speaker 1: they stand by themselves. I don't know what the correct
Speaker 1: 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
Speaker 1: you everything you need, run the test, break it down
Speaker 1: on the next test.
Speaker 3: Yeah. By the way, even the fact that you know
Speaker 3: a lot of the code has that so many marks
Speaker 3: were used were test statement to the fact that it
Speaker 3: was legacy code, because ideally, in more modern code I
Speaker 3: would use dependency injection and try to avoid so many marks.
Speaker 3: But again, the code is what it is, and that
Speaker 3: was the situation. So basically the thing was that before
Speaker 3: I could actually start the migration to Typescript, I actually
Speaker 3: needed to fix a bunch of mock leaks and get
Speaker 3: the tests to run properly in the original project before
Speaker 3: I even start migrating it over to typescript. So I
Speaker 3: verified that all the tests could run individually, and only
Speaker 3: then did I actually start the migration process. The next step,
Speaker 3: of course, is to install Typescript and initialize it. So
Speaker 3: you do youless you're using NPM or yarn or whatever.
Speaker 3: You NPM or yarn or MPM installed typescript, you do
Speaker 3: Typescript in it. And already here I ran into an
Speaker 3: issue because my default assumption was that the installation of
Speaker 3: Typescript should be as a dev dependency, because why would
Speaker 3: you need the actual Typescript compiler in the production environment.
Speaker 3: It's just part of your development pipeline. In order to
Speaker 3: actually build the system, the installable part of your project.
Speaker 3: You only actually deploy the disk folder or whatever it
Speaker 3: is that you're creating. And it turns out that at
Speaker 3: least in our case and maybe also in the case
Speaker 3: of our listeners, that was not the case. I actually
Speaker 3: had to install it as a PRAD dependency. Are you
Speaker 3: 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
Speaker 1: the PHP world on the back end with composer, where
Speaker 1: a classic a similar analog to what you're doing is
Speaker 1: PHP stand which is a static analysis tool for PHP.
Speaker 1: Where you don't want that, you know, in your in
Speaker 1: your PROD build, so you put it as a dependency
Speaker 1: use a save DEAV flag.
Speaker 3: Yeah. So unfortunately, I actually had to install typescript as
Speaker 3: a PROD dependency in our case. And the reason was
Speaker 3: that the build. The way that the built pipeline worked
Speaker 3: is that it actually created a pod for deployment, uh,
Speaker 3: you know, like a Docker pod basically, and and it
Speaker 3: actually did sort like a get pulled inside the pod
Speaker 3: and then ran the build in the pod. So if
Speaker 3: and it only did and it only ran the PROD installation,
Speaker 3: so if I didn't put it as product PRAD, I
Speaker 3: actually broke our CICD pipeline because it tried to run
Speaker 3: the TC as part of the PROD build, and it
Speaker 3: wasn't there. Now, ideally I should fix the CICD pipeline
Speaker 3: so that it doesn't work this way, maybe build it
Speaker 3: into a special environment and then copy the files over
Speaker 3: into the PROD environment rather than build in the PROD environment.
Speaker 3: But I wanted to reduce the amount of DevOps work
Speaker 3: that I actually had to do, and it seemed just
Speaker 3: a lot easier to just make Typescript itself a PROD dependency.
Speaker 3: It's just an ideal, of course, because you're shipping Typescript
Speaker 3: as part of your note modules. It's broad installation. But
Speaker 3: you know what, it's so big anyway, it didn't make
Speaker 3: that hole that much of a difference in our case.
Speaker 3: And as I said, I just wanted to get over
Speaker 3: this this blocker, so just.
Speaker 1: So just to clarify that, so it doesn't you know,
Speaker 1: I'm not a big Typescript user myself used some and
Speaker 1: 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,
Speaker 1: as you you know, you write your typescript code and
Speaker 1: then it compiles. It does I was checking it saves
Speaker 1: it to job script files, and that's what you want
Speaker 1: to build off of, isn't am I crrect?
Speaker 3: Yeah? Look, there there are really two ways these days
Speaker 3: in which you can run typescript on top of note,
Speaker 3: the way that you usually want to do in production
Speaker 3: environments is you want to run the Typescript compiler TSC.
Speaker 3: It runs, it does all the X, it fails if
Speaker 3: it identifies an error, but if everything is fine, it
Speaker 3: general like you said, it generates JavaScript files from the
Speaker 3: typescript files. So for every dot TS file you get
Speaker 3: a dot js file, and in that JS it's basically
Speaker 3: just essentially looked mostly like stripped out the type information.
Speaker 3: And then ideally you would just deploy those JS files
Speaker 3: and you don't deploy the TS files. But as I explained,
Speaker 3: the way that our CICD pipeline was built, because it
Speaker 3: was originally done before TSC, nobody thought about about that.
Speaker 3: The way that it worked was that it actually did
Speaker 3: a Git pull directly to the production pod that it
Speaker 3: was building, and then ran a quote unquote build on it,
Speaker 3: which effectively did very little in the pre TSC scenario.
Speaker 3: In the TSC scenario, that build actually runs the TSC compiler.
Speaker 3: So building the production pod required having TSC on that pod,
Speaker 3: so again not ideal. A better solution would be to
Speaker 3: fix the CICD pipeline, but that's how it was built
Speaker 3: when it was built, and I didn't want to mess
Speaker 3: about with the CICD job and building new one or whatever.
Speaker 3: As I said, I don't consider myself a dev's off person,
Speaker 3: and if I can avoid doing that, and I prefer to.
Speaker 3: By the way, these days, there's a different you can
Speaker 3: there's a different way for running typescript on top of Node.
Speaker 3: Actually there's a couple of ways. So first of all,
Speaker 3: there's this project called tsnode which literally wraps Node and
Speaker 3: adds TSC some support for typescript files into Node by
Speaker 3: effectively running TSC when it loads a file when files
Speaker 3: are loaded into Node, so you can use tsnode to
Speaker 3: run the typescript files directly on note without having to
Speaker 3: use TSC first to build the jobscript project out of it.
Speaker 3: But obviously this is not something you really want in
Speaker 3: production environments because all that thing adds a lot of
Speaker 3: overhead when the Node process starts, and why pay for
Speaker 3: that overhead. What the Node team has recently done is
Speaker 3: they've actually added a new command line switch, which kind
Speaker 3: of quote unquote gives Node TS support. The way that
Speaker 3: they do though, is not by running TSC, is by
Speaker 3: stripping out the type information. So think about like it's
Speaker 3: it's overwriting all the type definition with spaces, so it's
Speaker 3: kind of evaporating the type information out of the out
Speaker 3: of the TS files and then what you're left with
Speaker 3: is just JavaScript and it runs that, and it can
Speaker 3: do that fairly quickly. But on the conversely, it doesn't
Speaker 3: actually do any type checking. It just so they expect
Speaker 3: you to do the type checking beforehand, but they say
Speaker 3: you can still run the TS files directly and you
Speaker 3: hardly pay any over any overhead costs for that. Is
Speaker 3: that clear?
Speaker 2: Yes, sir, good.
Speaker 3: But again this is variatively new, relatively recent, and when
Speaker 3: I worked on that project, I didn't use it, so
Speaker 3: I I really needed to compile the TS file into
Speaker 3: JS files. So as I said, I installed TS, I
Speaker 3: initialized it, but I put it as part of the
Speaker 3: pro dependency, not a dev dependency because of the way
Speaker 3: in which our CICD pipeline is built. When you install
Speaker 3: an initialized when you initialize typescript, actually it generates a
Speaker 3: TS config file or TS configured dot json file to
Speaker 3: be precise, at the root of your folder of your project. Sorry.
Speaker 3: In fact, that's how TSC knows where the root of
Speaker 3: your project is. It assumes that where that file is
Speaker 3: located is the root of your project, and that ts
Speaker 3: configured dot json files is a JSON file obviously of
Speaker 3: configuration data for TSC telling it how to work how
Speaker 3: to process your project. For example, you can tell it
Speaker 3: which folders to include or exclude from its compilation. In
Speaker 3: our case, all the source files except for the root
Speaker 3: index file resided in an src folder, and all the
Speaker 3: test files the unit test files, and also some of
Speaker 3: the end two n test files resided in a test folder.
Speaker 3: I configured ts configure to compile both these to look
Speaker 3: at just these two folders, and by default TSC tries
Speaker 3: to compile of the files to be next to the
Speaker 3: original files. So if you have let's say a inn
Speaker 3: x dot ts, it will generate an x dot js
Speaker 3: alongside it. That is not what I wanted. The reason
Speaker 3: that this is not what I wanted is that my
Speaker 3: project is full of JS files and I want them
Speaker 3: to be processed as well, and I can't generate an
Speaker 3: x dot js file from an x dot JS file
Speaker 3: into the same folder I need. I have to generate
Speaker 3: it somewhere else. So I had to configure TSC to
Speaker 3: build everything into a disc folder, so it would take
Speaker 3: everything from the src and test folder and build them
Speaker 3: into a disc slash slash SRC and a disc slash
Speaker 3: slash sorry Test.
Speaker 1: Yes, So I don't know if I'm jumping ahead here,
Speaker 1: so pardon if I am. But is it normal practice
Speaker 1: to have like a TS file for the typescript and
Speaker 1: that compiles to JS or is that yeah?
Speaker 3: But I'm going to do Yeah, But like I said,
Speaker 3: I have one thousand JS files and I don't want
Speaker 3: to just arbitrarily rename all these files to dot TS.
Speaker 3: So from the way that I wanted to work is
Speaker 3: that is to manually transform each and every file at
Speaker 3: a time and do it gradually over time. So this
Speaker 3: project going all the way. If we ever go full TS,
Speaker 3: it might take years, and you know, but we'll, you know,
Speaker 3: every day or two will take a JS file and
Speaker 3: move it that particular file over to TS. But until
Speaker 3: we do, that file stays as JS. So I really
Speaker 3: need to have the TSC compiler take all the TS
Speaker 3: files but also also all the JS files Effectively, I
Speaker 3: needed to quote unquote compile the JS files as well
Speaker 3: as the TS files because I need them all to
Speaker 3: be compiled into the same folder, because otherwise I'll break
Speaker 3: the project structure. I can't just compile the TS files
Speaker 3: if I. If I have there a one thousand five
Speaker 3: files in this project that are JS and three files
Speaker 3: that I've moved over to TS, I need the resulting fold,
Speaker 3: the resulting this folder to have all the JS files,
Speaker 3: both from the originals and from the ones that I've
Speaker 3: 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
Speaker 3: build for speed. Incremental build means that that TSC maintains
Speaker 3: something like a cash and it sees that, hey, this
Speaker 3: file that I've previously compiled, nothing has changed in it,
Speaker 3: based let's say, on the time and date, so I
Speaker 3: don't need to compile it again. And this way you
Speaker 3: don't need to compile the entire project each and every time,
Speaker 3: even though in our case it was very very quick,
Speaker 3: precisely because most of the files it was just taking
Speaker 3: JS files and generating JS files from them almost as is,
Speaker 3: which apparently it can do very quickly. They specified the
Speaker 3: latest possible target based in terms of the JavaScript support,
Speaker 3: based on the no JS version that we're using. And
Speaker 3: since as I said, the project is using common JS,
Speaker 3: which means it uses a required rather than import, and
Speaker 3: also all the tests that we had did a lot
Speaker 3: of their mocking based on the assumption that require was
Speaker 3: being used, I can figure the output to be common JS.
Speaker 3: That means that in the ts files that we're creating,
Speaker 3: we can actually start using imports, but when the JavaScript,
Speaker 3: when the TSC compiles it into JavaScript, those import become required. Now,
Speaker 3: again this is less than ideal because ideally we would
Speaker 3: like to also move eventually from common JS over to ESM.
Speaker 3: But you know, one thing at a time, you can't
Speaker 3: do everything all at once. So the next thing that
Speaker 3: I did was allow JS files, since as I explained
Speaker 3: multiple times, we aren't actually converting most of the files
Speaker 3: yet most of the files remain JS. What it means
Speaker 3: by allowed JS files it means that the ts C
Speaker 3: will also process the JS files, effectively copying them over
Speaker 3: 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
Speaker 3: would happen That this folder will only contain the like
Speaker 3: the three JS files that were compiled out of the
Speaker 3: TS So sorry, I wouldn't be able to run the
Speaker 3: original because the original already contained some TS files and
Speaker 3: no doesn't know R to run them, at least without
Speaker 3: that special command line switch. The disc folder will just
Speaker 3: contain the JS files generated from those TS files, so
Speaker 3: most of the files will be gone, so neither the
Speaker 3: original source nor the disc fold there would be runnable.
Speaker 3: I wanted the disk folder to be runnable, which means
Speaker 3: that all the JAS files needed to be compiled quote
Speaker 3: unquote into that folder as well.
Speaker 1: So my point was just that you had to have
Speaker 1: this in conjunction with the earlier build step that copies
Speaker 1: everything to the separate discfolder. You'd get a whole lot
Speaker 1: of nothing.
Speaker 3: Basically. Yes, now, some of the code, some of the
Speaker 3: Jazz files actually required JSON files that were also located
Speaker 3: in the src folder with him. So what you also
Speaker 3: need to enable is the flag that tells TSC to
Speaker 3: handle to resolve Jason files. So it sees that a
Speaker 3: certain JavaScript or typescript files requires a jason file. It
Speaker 3: will also copy that Jason file over to that disk
Speaker 3: folder in the same relative location. And because if I
Speaker 3: initially neglected to do that, it didn't find those Jason
Speaker 3: f files when I try to run the project and
Speaker 3: that turned out to be bad. You can easily search
Speaker 3: your folder and see if you've got any files that
Speaker 3: don't match the dot js or dot jsx extension, and
Speaker 3: then you know that you need to make sure that
Speaker 3: these are copied over into the disc folder as well,
Speaker 3: and preferably by the TSC compiler itself. The next thing
Speaker 3: that I needed to do was enable source maps for debugging,
Speaker 3: since I still want to be able to debug the project,
Speaker 3: but now I'm actually running it from the disk folder,
Speaker 3: but I want to debug it in the typescript files.
Speaker 3: I want to see the original code that I wrote
Speaker 3: when I put in break points, not the compiled output code.
Speaker 3: It's very similar, but it's not identical, and I prefer
Speaker 3: debugging the original. So in order to achieve that, you
Speaker 3: enable source maps. Source maps are maps with the dot
Speaker 3: map extension that are generated by default in the same
Speaker 3: disk folder alongside the JS files. So if you had
Speaker 3: an x dot ts it gets compiled into the same
Speaker 3: relative location, and the disk folder into actually two files,
Speaker 3: one with the dot x dot JS and one with
Speaker 3: called x dot map. And actually at the very bottom
Speaker 3: of the JS file, it adds a special comment that
Speaker 3: tells dev tools where that where the map file is located.
Speaker 3: And in this case it's easy because it's located in
Speaker 3: exactly the same place, so it's a simple relative location.
Speaker 3: Map files enable debuggers like dev tools, which is built
Speaker 3: into Chrome or Edge or effectively any dev tools of
Speaker 3: any debugger any browser, sorry or the built in the
Speaker 3: buggers in most IDs, to be able to single step
Speaker 3: through the original files because the debuggers is smart enough
Speaker 3: to understand that this line in this file is actually translated.
Speaker 3: It matches that line in the original file, and here's
Speaker 3: the location of that original file relative to the compiled
Speaker 3: output file. The next thing I enabled was strict type checking.
Speaker 3: I was kind of concerned about that, but there was
Speaker 3: no problem with it, so I used that. And I
Speaker 3: also in our case, because we're using common JS, I
Speaker 3: made sure to enable the flag that instructs TSC to
Speaker 3: always put you strict at the type of at the
Speaker 3: top of the generated files. If you're using ESM, you
Speaker 3: don't need it because ESM files arem module files are
Speaker 3: strict by default. But in our case sense again, since
Speaker 3: everything was compiled to common JS, I really wanted to
Speaker 3: have you strict there to get strict behavior.
Speaker 1: Can you elucidate on what strict means or what it's
Speaker 1: going to do.
Speaker 3: Yeah, Strict is a very old JavaScript feature, dates back
Speaker 3: to US six or before. Basically, there was certain let's
Speaker 3: call it less than ideal behaviors in JavaScript that they
Speaker 3: unfortunately could not remove out of the language due to
Speaker 3: backward compatibility issues, and the way that they decided to
Speaker 3: fix them was add this use strict directive that if
Speaker 3: you put it at the top of your file, for example,
Speaker 3: it's just a string quote open quote U strict close quote.
Speaker 3: It puts the JavaScript engine in strict mode, which changed
Speaker 3: just certain those certain bad behaviors a couple of examples.
Speaker 3: The with statement turned out to be a bad idea
Speaker 3: in JavaScript for various reasons. With you strict with is gone.
Speaker 3: There's also a difference in what happens if you try
Speaker 3: to use this outside the context of an object. You know,
Speaker 3: if you just have a function and it does this
Speaker 3: dot x before you strict, it would default to the
Speaker 3: global object with stricted defaults to undefined. So they they
Speaker 3: made certain changes that improved the behavior of JavaScript, but
Speaker 3: they were not backward compatible, so they had to add
Speaker 3: the TC thirty nine way back when had to add
Speaker 3: this flag in order to support it. Now in modern
Speaker 3: environment like EES models, if you're using ESM, then by
Speaker 3: by definition you're using e S six, then you you're
Speaker 3: they can assume that you're US strict by default because
Speaker 3: there are no backward compatibility issues there. Likewise, if you're
Speaker 3: inside of a JavaScript class, again it's modern JavaScript by definition,
Speaker 3: so they can assume you strict within that class, regardless
Speaker 3: of whether you put ustrict that in the top of
Speaker 3: the file or not. And the last thing that I
Speaker 3: needed to add was to skip lib lib tests, which
Speaker 3: checks that you have type definitions for all the LIBS
Speaker 3: libraries that you're using, because you were using some old
Speaker 3: and even almost deprecated libraries which and especially some internal
Speaker 3: libraries did not have type information, which means that did
Speaker 3: they did not have a d dot t S files.
Speaker 3: If you're familiar with the concept, yes, we.
Speaker 1: Would clarify that lib is not liberal versus conservative, that
Speaker 1: is library.
Speaker 3: Yeah, exactly. So you've got a library let's say let's
Speaker 3: say you have a library and it was compiled when
Speaker 3: it was built from typescript to JavaScript, and you are
Speaker 3: only distributing that library because it's it's an NPM package.
Speaker 3: You're only distributing it as the compiled output the JAS files.
Speaker 3: But you still want the type information, so that code
Speaker 3: that uses that library can can only call library APIs
Speaker 3: with your passing in the correct parameters of the correct
Speaker 3: types and stuff like that. So what the Typescript compiler
Speaker 3: can do is it generates a file with a dot
Speaker 3: D dot ts extension, and that file contains type definitions
Speaker 3: for that library, so that Typescript knows to verify that
Speaker 3: you're using that library correctly. But some of our own libraries,
Speaker 3: especially internal libraries, were old, did not have this support,
Speaker 3: and unless I told it to skip the library tests,
Speaker 3: it would fail because they would say, hey, you're trying
Speaker 3: to use this library. I'm looking for type information for
Speaker 3: this library and I can't find it. I can't use it,
Speaker 3: you know, abort. By setting that skip lip tests, I
Speaker 3: was able to tell TSC, you know, okay, it is
Speaker 3: what it is, just deal with it. Give the things
Speaker 3: that you import from that library type any effectively, well
Speaker 3: not effectively literally now, since all the JavaScript files are
Speaker 3: also processed as I explained. As I said, I needed
Speaker 3: to specify everything to go into a disk folder to
Speaker 3: avoid collisions. And obviously you need to add this disc
Speaker 3: folder to your get ignored for example, because you don't
Speaker 3: want to save that disc folder into git up for example,
Speaker 3: because it's you know, you don't ever put in git
Speaker 3: auto generated files. The next thing I wanted to do
Speaker 3: is install types for various libraries that I use, So
Speaker 3: you basically NPM install or arn't add things like at
Speaker 3: types slash node or at types slash Express. Basically there
Speaker 3: are a lot of these type these things that you
Speaker 3: can install or that are at types slash something based
Speaker 3: on the service or library that you use, and they
Speaker 3: just make sure to add the type information for those
Speaker 3: various libraries and services. So now that if I use
Speaker 3: a Node API, I get the type checking for that
Speaker 3: Node API, or likewise for Express, which is really great
Speaker 3: and what we really want Typeescript for. Since I had
Speaker 3: to put Typescript itself as a prod dependency, I unfortunately
Speaker 3: had to put these as a proud dependency as well.
Speaker 3: In your case, if your CICD allows it. You can
Speaker 3: put both d S and these type these type packages
Speaker 3: as as dev dependency sorry, assuming again your CICD pipeline
Speaker 3: supports it. I also needed to install a package called
Speaker 3: source slash map slash support to get the proper debugging
Speaker 3: support in all the environments that I use with the
Speaker 3: map files that have already configured. The generation of a
Speaker 3: lot of steps and again not trivial. I for some
Speaker 3: reason I couldn't really find Maybe our listeners are better
Speaker 3: at they have a stronger Google for AI food than
Speaker 3: I have and can find this information. I could not
Speaker 3: find a clear description of all this information. The next
Speaker 3: step that I had to do is I had to
Speaker 3: do with eslint. So I assume or hope that you're
Speaker 3: using islint in your project. If you do. If you don't,
Speaker 3: then you should. We had guests on our show talking
Speaker 3: about eslinteslint is a linting tool that verifies that you're
Speaker 3: using the language JavaScript according to best practices you know,
Speaker 3: either generally accepted or ones that you've decided on within
Speaker 3: your organization. It could be almost comical rules like you know,
Speaker 3: prefer quote single quote over double quotes, so you don't
Speaker 3: have a random mixture of both types of quote in
Speaker 3: your project. Or it can be about things like putting
Speaker 3: the curly brackets at the end of the line rather
Speaker 3: at the beginning of the next line, and so on
Speaker 3: and so forth, everything that you prefer in terms of
Speaker 3: coding style and avoiding legal but bad patterns within your code. Now,
Speaker 3: if you're using Typescript, you also want to install something
Speaker 3: called t s slint or Typescript e slint. We had
Speaker 3: Josh Goldberg on our show to talk about why you
Speaker 3: want this. Basically, it's es slint for your Typescript. Now.
Speaker 3: You need it for two reasons. The obvious reasons is
Speaker 3: that it adds particular specific linting rules for Typescript itself.
Speaker 3: I mean obviously because similarly to JavaScript, there can be
Speaker 3: things that are legal in Typescript, but you nonetheless prefer
Speaker 3: to avoid because there are sort of anti patterns in
Speaker 3: the language. But it actually goes beyond that. It turns
Speaker 3: out without t S slint, eslint itself will flag certain
Speaker 3: things in your Typeescript code as bugs because they don't
Speaker 3: match lint JavaScript linting rules. For example, you can use
Speaker 3: extends in JavaScript in TS in Typescript generics, but it
Speaker 3: assumes that extends its used for extending JavaScript classes and
Speaker 3: that you're using it wrong and it flags it. So
Speaker 3: you need t S e S Flint for the linting
Speaker 3: to work properly. Now, if you're adding t S slint,
Speaker 3: you need to make sure that you're using the latest
Speaker 3: version of eslint. Unfortunately, because we were a legacy project
Speaker 3: in our case, this was not the case. So I
Speaker 3: had to update eslint to the latest version. And then
Speaker 3: I found out the hard way that they the Slint project,
Speaker 3: made a significant, not backward compatible change in the way
Speaker 3: in which they managed configuration. They went to a flat
Speaker 3: configuration file. They basically changed the whole structure of the configuration.
Speaker 3: So I had to go through the process of fixing
Speaker 3: the configurations and as long as I was there, rethinking
Speaker 3: the configurations because a lot of the rules were dated,
Speaker 3: so I updated our Slint rules as a part of
Speaker 3: this migration process. As you can see a lot of work.
Speaker 3: The next issue that I ran into was the fact
Speaker 3: that some people on our team prefer using vis Code
Speaker 3: and some people in our team preferred using web Storm
Speaker 3: for JavaScript development, and I wanted to support both I
Speaker 3: didn't want to force people to have to switch their
Speaker 3: development environment, which means that I had to get at
Speaker 3: everything working in both. And where this ran into a
Speaker 3: problem was with the unit tests. Web Storm out of
Speaker 3: the box had this ability, has this great ability that
Speaker 3: it can actually scan your files, find the unit tests,
Speaker 3: and make it possible to run individual unit tests and
Speaker 3: debug individual unit tests directly from within the web Storm environment.
Speaker 3: You can actually do the same thing in VS code,
Speaker 3: but you actually need to install an appropriate extension first.
Speaker 3: Once you install the extension, it's great. I actually prefer
Speaker 3: vis code with the extension of a WebStorm, but that's me.
Speaker 3: We had developers who preferred using WebStorm, and for the
Speaker 3: life of me, I could not get WebStorm to handle
Speaker 3: the situation in which the TS file files were located
Speaker 3: in one place and the output JS files were located
Speaker 3: in a different place in the disfolder and to figure
Speaker 3: out that even though the test code the source code
Speaker 3: for the test was here, it should actually run the
Speaker 3: code that's over there and use the map files to
Speaker 3: properly support the single step and debugging with the VS code.
Speaker 3: And as I said, we were using Mocha and Shi
Speaker 3: on VS code. I was able to install those the
Speaker 3: vs code extensions for Moca properly configure it. It just worked.
Speaker 3: In that scenario WebStorm, I could not get it to work.
Speaker 3: The only way in which I finally was able to
Speaker 3: get it to work. As I mentioned before, this tsnode
Speaker 3: thing where it's a wrapper to node that teaches node
Speaker 3: how to handle TS files. So I actually had to
Speaker 3: configure the template file for Moca on WebStorm to use
Speaker 3: tsnde instead of nodes, and then it worked. So I
Speaker 3: used a different typical configuration between web Storm and between
Speaker 3: vs code and vis code. I would I preferred the
Speaker 3: test to run closer to the way that they run
Speaker 3: in the CICD pipeline. That means to run the test
Speaker 3: and the compiled tests and the disfolder. And as I said,
Speaker 3: I could get I could get it to work with
Speaker 3: the source maps because the extension from Mocha was smart
Speaker 3: enough to handle it. In the case of WebStorm, I
Speaker 3: don't know if it can or it can't. I could
Speaker 3: not figure it out. I had to use ts node.
Speaker 2: Now you can still run the test from the command line,
Speaker 2: Is that right?
Speaker 3: Yeah?
Speaker 1: You're talking about is like the UI you know, it
Speaker 1: gives you a little button you can press run this test.
Speaker 3: Exactly exactly which you want if you want to debug tests,
Speaker 3: because when you have that button, you can actually right
Speaker 3: click it select debug instead of run, and then you
Speaker 3: actually single step that test within your ID, which you know,
Speaker 3: if you have a failing test, the ability to debug
Speaker 3: that failing test makes life a whole lot easier. It's
Speaker 3: certainly much more, much more fun than console log debugging exactly.
Speaker 3: And the other thing I needed to do was configure
Speaker 3: the Mocha itself to run all the tests from this
Speaker 3: test instead of tests. One more thing, and now I
Speaker 3: had to start modifying scripts in the package json itself.
Speaker 3: You know, we had all the built scripts and run
Speaker 3: scripts and dev scripts, so I added the script for example,
Speaker 3: a built script so that you could run TC as
Speaker 3: NPM run build or Yarn build would literally just run TSC.
Speaker 3: But I also wanted to support a more dynamic mode
Speaker 3: for development. You know, in development, you want for example, node,
Speaker 3: Node has this ability to watch your folder, see that
Speaker 3: it changes, and then restart itself whenever it identifies that
Speaker 3: the file is changed. But now it becomes a more
Speaker 3: complicated scenario because what actually needs to happen is you
Speaker 3: save that file. You want that file. You want ESC
Speaker 3: to notice that the file is changed and then compile
Speaker 3: it to the disk folder. And then Node you want
Speaker 3: it to notice that the contents of the disk folder
Speaker 3: is changed and to restart itself. So it becomes like
Speaker 3: a two step instead of a one step. Is that clear, crystal?
Speaker 3: So initially I did the naive thing. There was dev
Speaker 3: script in the package Jason that literally had uh node
Speaker 3: running Node in watch mode. I forget the syntax. Let
Speaker 3: me take a quick look at how it actually is implemented.
Speaker 3: So it had it had it used nodemon with a
Speaker 3: watch to actually automatically reload Node whenever the content of
Speaker 3: the folder change. And originally it was running things directly
Speaker 3: from the source folder. I wanted noemond to look at
Speaker 3: the disc folder instead. That was easy enough, but I
Speaker 3: also wanted uh TSC to look at to watch the
Speaker 3: source folder and see if it changed, to compile automatically
Speaker 3: into the death folder. And there is a TSC minus
Speaker 3: minus watch that you can use for that. So originally, initially,
Speaker 3: so one thing you can do just run those two
Speaker 3: commands separately. You can open like two terminals, or you
Speaker 3: can use in vs code there's this thing that they call,
Speaker 3: I forget the name tasks you can do. You can
Speaker 3: run both two tasks, one to have TSC watching your
Speaker 3: source and node more watching your disc. But I did
Speaker 3: want to have to do to force our developers to
Speaker 3: do these two steps, where in the past they just
Speaker 3: did one. In the past, they would just do yarn
Speaker 3: dev and it just worked, and I wanted to work
Speaker 3: this way. So initially I was kind of naive and
Speaker 3: I did TSC minus minus watch, then hun percent unpercent
Speaker 3: node mon minus minus watch, and it didn't work. Can
Speaker 3: you guess why it didn't work. It didn't work because
Speaker 3: un percent unpercent waits for the process on the left
Speaker 3: to finish successfully before running the process on the right.
Speaker 3: 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
Speaker 3: finish before running a nodemond. But TSC minus minus watch
Speaker 3: never finishes. That's the whole point. It runs continuously in
Speaker 3: order to watch for changes in the source file never
Speaker 3: so it never actually ran the nodemd Oh. Yeah. So
Speaker 3: the easiest solution that I finally found for this is
Speaker 3: another package called concurrently, which you can tell it to
Speaker 3: run both things simultaneously and kill them both if you
Speaker 3: do a control C. So look at a package called concurrently.
Speaker 3: That's what I used to fix it. Maybe there's a
Speaker 3: smart maybe smarter Bash gurus can do it without it.
Speaker 3: That was the easiest solution for me. That's what I
Speaker 3: ended up using. Notice one important thing that I have
Speaker 3: not done so far at all, and that's kind of important.
Speaker 3: I wanted to emphasize this. All this work was done
Speaker 3: before I converted even one JS file to Typescript. So
Speaker 3: I've got all this pipeline working and TSC working and whatnot,
Speaker 3: but it's basically compiling JavaScript files to JavaScript files. I
Speaker 3: don't have a single TS file in the project yet.
Speaker 3: And that's really important because initially I kind of when
Speaker 3: I kind of got ahead of myself and I started
Speaker 3: moving JS files to TS in order to see that
Speaker 3: things worked before I had everything properly working, and then
Speaker 3: I ran into all sorts of problems, which eventually ended
Speaker 3: up with me throwing the whole thing away and starting
Speaker 3: from scratch. So make sure you've got everything working so
Speaker 3: far before moving even a single JS file to TS,
Speaker 3: because otherwise you'll be sorry. So create a PR from
Speaker 3: everything that you've done so far, make sure that everything
Speaker 3: according to what I explained. Make sure that your CI,
Speaker 3: your updated CICD now passes while running TSC again without
Speaker 3: converting any TS file yet at all.
Speaker 1: And I didn't with the assumption being that when you
Speaker 1: do convert TS files and everything's in place, right, you haven't.
Speaker 1: Everything you've done is to handle the conversion of the
Speaker 1: typescript files to jobascript exactly.
Speaker 3: I wanted to get to a situation where everything else
Speaker 3: worked and then I can start converting files individually and
Speaker 3: not have to worry about it. I didn't want to
Speaker 3: deal both with the configurations and the conversions at the
Speaker 3: same time. So I created a PR. I verified that
Speaker 3: the whole CI process passed for that PR. I didn't
Speaker 3: merge that PR. I just wanted to see that it
Speaker 3: was green. Yes, after I saw. After I saw that
Speaker 3: it was green, I moved one and just one file
Speaker 3: from being a jazz file to a TS file and
Speaker 3: 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,
Speaker 3: it would complain about various missing type declarations and whatnot.
Speaker 3: I can obviously set it to super relaxed, not the
Speaker 3: strict mode, but I intentionally wanted strict mode. So I
Speaker 3: actually I chose a really small JS file, literally one
Speaker 3: of the smallest files in the project that I can found,
Speaker 3: one that was not dependent on any other project, a
Speaker 3: file in my project. So it was literally a leaf
Speaker 3: file only it may depend on external packages, but not
Speaker 3: on anything inside my own project. And I just converted,
Speaker 3: but I made sure that that jazz file was actually
Speaker 3: a file that's being used. I didn't want to convert
Speaker 3: something that nob the actually imports or requires, uh, And
Speaker 3: I converted just that JS file D two ts. I
Speaker 3: did it using get m vy or get move in
Speaker 3: 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
Speaker 1: about get, At least to me, it seems a little weird.
Speaker 1: Maybe it makes sense when you think about it. If
Speaker 1: you want to rename a file, it's not a renamed command.
Speaker 1: It's a move command to move it from one file
Speaker 1: to another.
Speaker 2: Yeah, kind of the same making a branch, same thing
Speaker 2: with the branch exactly.
Speaker 3: So I did a geit move of that a single
Speaker 3: file from JS to TS, preserving its history. I added
Speaker 3: the type information. I made sure that that it's a
Speaker 3: file that's actually as I said. Then give the rules.
Speaker 3: All file as small a file as I can find,
Speaker 3: not dependent on any the other file within my own
Speaker 3: within that particular project, but can be dependent on external things.
Speaker 3: Is used by one of the unit tests again either
Speaker 3: directly or indirectly, but small and simple, and is required
Speaker 3: by somebody again by a by a unit test or
Speaker 3: by and buy some code and and converted that. Added
Speaker 3: the type information. UH, make it as simple as possible,
Speaker 3: avoid changing execution logic, business logic. Just add the type information,
Speaker 3: committed it, committed that change. UH, created a commit for
Speaker 3: that change, updated the PR and saw that it still worked.
Speaker 3: And happily you did so.
Speaker 1: You mentioned you said when I UH mentioned about just
Speaker 1: 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
Speaker 1: something else that you have to put in there that
Speaker 1: says no, you.
Speaker 3: Need to put it in the type information and unless
Speaker 3: you make as I said, as I mentioned when I
Speaker 3: created the TS CONFIGU Todd jason file. I set it
Speaker 3: to strict type checking. I could have not put it
Speaker 3: a strict type checking, and then I could have just
Speaker 3: renamed that file. But I put it a strict which
Speaker 3: means that it expects to see type information on things
Speaker 3: like variables. Unless it can in further type information, it
Speaker 3: needs you to give it the type information. So if
Speaker 3: you do a const x equals three, it knows that
Speaker 3: x is a number. But if it's just let x
Speaker 3: without signing anything, it can't know the type of X,
Speaker 3: So you need to be explicit about it. Likewise, if
Speaker 3: you've got arguments, you need to tell it type of
Speaker 3: the arguments. It can in further return value, but you
Speaker 3: need to give it the types of the of the
Speaker 3: function parameters or function argument. So when I renamed the
Speaker 3: TF file from JS tots, I also needed to put
Speaker 3: in the relevant type information. After all, that's the whole
Speaker 3: point of moving from JS tots, and I wanted to
Speaker 3: see it handle that type information. And as I said,
Speaker 3: I moved just that one file. I pushed it in
Speaker 3: and verified that everything that all the build works, that
Speaker 3: the unit tests work, that the end to end test work,
Speaker 3: that deployment is created, that I can actually use that
Speaker 3: created deployment, and I even looked in the disk folder
Speaker 3: to verify that the JS file was created for that
Speaker 3: TS file, and then it contained what I expected it
Speaker 3: to contain. So it was that that anal about it, really,
Speaker 3: just because again it was that first file. After that
Speaker 3: you can be more relaxed than your attitude. I then
Speaker 3: intentionally introduced a typescript error into that file to see
Speaker 3: that it failed. Because if you don't see that everything fails,
Speaker 3: how do you know that it actually even working and
Speaker 3: not just doing something stupid. So I put in intentionally
Speaker 3: a typescript error. Shane, let's say something supposed to be
Speaker 3: a string. I said that it's a number, and saw
Speaker 3: that typescript you know, failed and the build failed, And
Speaker 3: then I fixed it again and saw that the build
Speaker 3: passed again, and I and I and as I said,
Speaker 3: I pushed it into that branch and saw that the
Speaker 3: CI process failed, passed, sorry, and everything was green. But
Speaker 3: again I didn't merge it yet. Instead, I deployed it
Speaker 3: to our testing environment, and I made sure that I
Speaker 3: could properly debug it both using vis code and web Storm.
Speaker 3: We've got a rather complex complicated for various technical reason
Speaker 3: environment for debugging, which involves remote deployments and stuff like that.
Speaker 3: It's not really important in the context of this discussion.
Speaker 3: I won't go into the details. I then asked some
Speaker 3: of my colleagues to test out that branch as well,
Speaker 3: see that they're also able to use it and debug
Speaker 3: it and that everything just works for them. Again, I
Speaker 3: was very very careful about it. After I got the
Speaker 3: okay from those colleagues, I rebased the branch, and because
Speaker 3: we use rebase rather than merge, because it took me
Speaker 3: a while, so obviously other changes were done in the
Speaker 3: project during that time. So I rebased and finally merge
Speaker 3: that pr and finally we had a version that supported
Speaker 3: Typescript and had one and exactly one TS file in
Speaker 3: that project out of those one thousand JavaScript files. And
Speaker 3: I also made sure not to have to do that
Speaker 3: right before the weekend, so that I don't break everything
Speaker 3: just before the weekend.
Speaker 1: Well that's the general rule anyway, right, you don't do
Speaker 1: major deployments on Friday.
Speaker 3: After merge, I verified that the main branch again was
Speaker 3: also built correctly, that all the end to end test paths,
Speaker 3: that everything worked correctly, I was ready to roll back otherwise.
Speaker 3: Fortunately I did not have to. I then basically informed
Speaker 3: everybody else about this change, and we started discussing about
Speaker 3: how we can gradually move from JS to TS. And
Speaker 3: we've been doing it very slowly since then, and that
Speaker 3: more or less concludes where we are now. And it was,
Speaker 3: as I said, it was a lot less trivial than
Speaker 3: I expected it to be. Now some of the issues
Speaker 3: had to do with our own unique environment. But still
Speaker 3: now I have heard other suggestions about how to go
Speaker 3: about it. For example, convert your repo into a mono
Speaker 3: repo type environment, start pulling out self contained files as
Speaker 3: separate packages within that mono repo, and since there are
Speaker 3: new packages, just make them TS from the get go.
Speaker 3: You can do that. You still need to be very
Speaker 3: careful about how you preserve history and stuff like that.
Speaker 3: It's not trivial, but that's not what I did in
Speaker 3: this project. Anyway, I might actually have to do that
Speaker 3: other way in that project with JavaScript and flow, because
Speaker 3: I don't take I can get both JavaScript and flow
Speaker 3: to work within the same package. Anyway, I'm done.
Speaker 1: So, how many files have you actually now that all
Speaker 1: that's in place, how many files that you actually.
Speaker 3: Converted not so many so far, approximately ten out of
Speaker 3: the one thousand. This will take time. The biggest problem
Speaker 3: in converting files is that, well, you know, sometimes you
Speaker 3: run into complicated types that require generics and stuff like that,
Speaker 3: and you need to get the team up to speed
Speaker 3: about typescript because a lot of them have been basically
Speaker 3: just using javascripts, so they're not as familiar with typescript
Speaker 3: as ideally they should be, and you need to allocate
Speaker 3: time for that as well. But the bigger issue is
Speaker 3: that the move to typescript has actually uncovered various bugs
Speaker 3: in the code, places where null tests were missing. So
Speaker 3: one of the great things about typescript is the typeescript
Speaker 3: basically having is explicit about something being that can be null.
Speaker 3: So it's something is can be either if something is,
Speaker 3: then if a variable is of type number, it cannot
Speaker 3: be null. It you basically, if you wanted to also
Speaker 3: be null, you need to explicit be explicit about and
Speaker 3: say number or null. And it uncovered a lot of
Speaker 3: places in the code that null tests were not properly
Speaker 3: put in place, or places where library third party libraries
Speaker 3: were used and a version was updated that changed the API,
Speaker 3: but the way in which that library was used, wasn't updated,
Speaker 3: and the wrong parameters were actually passed into certain APIs.
Speaker 3: Now somehow it worked, but now Typescript complains about it.
Speaker 3: Then you have to fix it, and you have to
Speaker 3: think about, you know, how do I fix it? It
Speaker 3: actually requires changing the actual execution code in order to
Speaker 3: get Typescript to be happy about it. If you're passing
Speaker 3: something which is a number into something that should be
Speaker 3: a string and it happens to work, you know, you
Speaker 3: don't just want to cast a number a string. You
Speaker 3: want to fix it. You want it to work properly.
Speaker 3: So the biggest time consumer really so far has been
Speaker 3: basically fixing bugs that Typescript uncovers.
Speaker 1: Yeah, I've been so I do a lot of work
Speaker 1: leral on PHP in my day to day, and so
Speaker 1: the past couple of weeks I've been working with PHP STAN,
Speaker 1: which is a PHP static analysis tool, hence the name STAN.
Speaker 2: And STAN. I definitely have a love a lot of
Speaker 2: hate relationship with it. Well.
Speaker 1: A couple of things that I found out is one
Speaker 1: cases you're correct, it does the similar thing where you
Speaker 1: have to declare okay, it can be the this or no.
Speaker 1: You know, I have to clear the options well, a
Speaker 1: lot of times, what I find myself having to do,
Speaker 1: and I'm curious to see if you've run into this
Speaker 1: as well, is having to restructure working code for the tool.
Speaker 1: So in other words, if if you have a step
Speaker 1: that has that you know, does some chaining and does
Speaker 1: multiple things, sometimes you have to break that apart so
Speaker 1: the static analysis tool can realize, okay, this variable is
Speaker 1: of this class and then you can go on into
Speaker 1: it where it works fine, you know with with when
Speaker 1: things are chained together, but you have to break things
Speaker 1: apart strictly for the static analysis tool. That was probably
Speaker 1: one of the bigger frustrations that I've you know, in
Speaker 1: dealing with that, that I've had to fight.
Speaker 3: Look you can, yeah, but I'm thinking about how to
Speaker 3: phrase it. First of all, I'm not familiar with PHP
Speaker 3: stand but I can say that typescript is amazing in
Speaker 3: the in the sophistication of the static analysis that it
Speaker 3: can perform. So, for example, if something is if a variable,
Speaker 3: as I said, is number or null, and then you
Speaker 3: do an if in the code and check that it's
Speaker 3: not null, then it knows that within the scope of
Speaker 3: that if the type of that variable is now assumed
Speaker 3: to be number and not number or null. It's really
Speaker 3: smart about how to infer the correct type. So the
Speaker 3: value that the type of of of a variable actually
Speaker 3: changes according to the code because of various type checks
Speaker 3: that can exist in the in the runtime code. These
Speaker 3: are known as type guards. We actually talked about in
Speaker 3: the in the recent episode that we did with the
Speaker 3: Lyran and Ariel. You were in that episode, I believe, right, yes, yeah,
Speaker 3: so we actually talked about that. So Typescript is amazingly
Speaker 3: sophisticated about such things. The fact that you need to
Speaker 3: break the code into smaller pieces. From my perspective, that's
Speaker 3: actually a benefit. I like my code broken down in
Speaker 3: smaller pieces.
Speaker 1: Right, and I tend to be that way too. You
Speaker 1: don't want to, you know, have fifteen chain functions on there,
Speaker 1: just for the sake of having shorter code. If a
Speaker 1: little more broken up code is easier to read. But
Speaker 1: when it's already working, you got to break it up
Speaker 1: just for the tool, it can be frustrating.
Speaker 3: I agree with that, and that exactly goes to the
Speaker 3: point that I mentioned that the biggest challenge so far
Speaker 3: has been those situations in which we actually had to
Speaker 3: change the JavaScript in order for Typescript to be happy
Speaker 3: about it, and in most cases the change was for
Speaker 3: the better. It was a good change, but it's still work.
Speaker 3: And once you know ideally, you're thinking, I'm just adding types,
Speaker 3: so all the tests should pass because the types are
Speaker 3: removed from the generated code anyway, so they don't impact
Speaker 3: the tests. But once you have to start modifying the code,
Speaker 3: you also start breaking tests, which is another reason why
Speaker 3: 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
Speaker 1: this on your internal wiki, and we've got this episode obviously,
Speaker 1: but are you going to put this out in written
Speaker 1: 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,
Speaker 3: but I just couldn't find it, but it probably is.
Speaker 3: Probably should. The only problem is that I I don't
Speaker 3: really have a blog anymore, so I need to figure
Speaker 3: out about where to actually put it. I'm also, as
Speaker 3: I mentioned, I think I may want to submit it
Speaker 3: as a conference talk, not that you know it prevents
Speaker 3: I can do all three. You know, we now have
Speaker 3: a podcast. I can have it as a blog and
Speaker 3: as a proposed conference talk. I just need to figure
Speaker 3: 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
Speaker 1: talk about anything else, or we can talk about tech
Speaker 1: if we want. I'll give Dan throw to break since
Speaker 1: he's been doing most of the talking here, and do
Speaker 1: my dad jokes of the week. Maybe we'll get a
Speaker 1: laugh from Damn. Maybe not so recently. You know, I
Speaker 1: had to change a password, and I normally use a
Speaker 1: password manager, but this particular time, we'll say this was
Speaker 1: back before password managers. I need a password that had
Speaker 1: eight characters, so I did snow White and the Seven
Speaker 1: Dwarfs because seven plus one is eight. So I've got
Speaker 1: three kids, and when our last one was born, my
Speaker 1: wife was in labor and I tried to tell her
Speaker 1: jokes to distract her from the pain, but she didn't
Speaker 1: last at all. It must have been the delivery, right man. Yeah,
Speaker 1: this is this is a recent one. I love it
Speaker 1: because it's somebody pointed out to me on Twitter it's
Speaker 1: both funny and inspirational. Says when you think that all
Speaker 1: is lost, there's no hope left. Just remember the lobsters
Speaker 1: 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
Speaker 2: of the week. Dan, you got any picks for us?
Speaker 3: Yeah, not that many, but a few. So one pick
Speaker 3: I have is a friend of mine here in Israel
Speaker 3: called the lead Josef great confidence speaker by the way,
Speaker 3: So he created a very interesting project. There's he actually
Speaker 3: put on X the whole story, a thread about the
Speaker 3: whole story about how they built this thing. It's an
Speaker 3: incredible read. Unfortunately it's an Hebrew I know that it
Speaker 3: has a built in transition capability, but still but anyway,
Speaker 3: this this really interesting thing is called get mcph. I
Speaker 3: spoke about MCP with Jack Hankton when we had him
Speaker 3: as a guest on the show. MCP I forget what
Speaker 3: it's what it stands for, but it's basically a standard
Speaker 3: protocol for connecting stuff to llms. So the llms can
Speaker 3: actually use that protocol to get additional context, additional information,
Speaker 3: or to control various external resources. So they created something
Speaker 3: called get MCP, which basically makes it possible to instantly
Speaker 3: create an MCP server from any getub project and then
Speaker 3: you can hand it over to something like Cursor and
Speaker 3: immediately Cursor has context based on that getub project, which
Speaker 3: is really interesting. So if you want to learn about
Speaker 3: some getub you know, you can obviously clone a getub
Speaker 3: project and open it within you within Cursor and then
Speaker 3: you get context. But suppose you want to you're working
Speaker 3: on your own project that uses some other project APIs
Speaker 3: from some other project, and you want Cursor to be
Speaker 3: able to help you generate code for using those APIs. Well,
Speaker 3: with get MCP you can just point. It's literally just
Speaker 3: replacing the word GitHub in the URL with geitub dot
Speaker 3: com with get mcp dot io. That's literally it. That
Speaker 3: URL will immediately work. You just hand over that URL
Speaker 3: to Cursor, for example, and then and instantly Cursor has
Speaker 3: context about that project. It seems like magic to me.
Speaker 3: I've not tried it myself yet, but it literally seems
Speaker 3: like magic. Some of the videos that they have on
Speaker 3: their website literally seem like magic. It's it's pretty amazing
Speaker 3: the stuff that you can do. These days with these tools.
Speaker 1: So MCP stands for you mentioned the CEE. It's a
Speaker 1: model context protocol.
Speaker 3: Right, it's a this protocol proposal that's supposed to be
Speaker 3: a new standard for connecting llms to stuff, and uh,
Speaker 3: it's pretty amazing what they've achieved. So it works with Claude,
Speaker 3: it works with Cursor, it works with Windsurf, it works
Speaker 3: with vis code. As I said, I've not tried it yet.
Speaker 3: I've only learned about it effectively yesterday and I haven't
Speaker 3: had a chance to play with it, but it seems
Speaker 3: pretty amazing the fact that you can literally just by
Speaker 3: changing replacing the word in the URL from getub to
Speaker 3: get MCP and immediately get it to work, it's just
Speaker 3: amazing to me. And the other pick is I think
Speaker 3: I mentioned that I'm speaking at a conference in Romania
Speaker 3: towards the end of May. It's called JS Heroes. I'll
Speaker 3: be talking about how modern web frameworks use or prefer
Speaker 3: using RPC as the communication protocol between the front end
Speaker 3: and their back ends. If you're thinking about frameworks like
Speaker 3: next JS or like quick or like solid, they're all
Speaker 3: or ten stack. They're they're kind of moving over to
Speaker 3: using our PC, So I'm talking about what RPC is
Speaker 3: and why they're doing it, and what are the implications,
Speaker 3: the good sides the bad sides of this transition. And
Speaker 3: the thing is that, first of all, I have to
Speaker 3: say that Jas Heroes Conference, the agenda looks amazing, The
Speaker 3: speaker lineup is really awesome. The interesting thing, though, is
Speaker 3: one of the organizers is Tagus. We've had Tags on
Speaker 3: their show a few times. Tags is an amazing person,
Speaker 3: and he kind of tweeted recently that a lot of
Speaker 3: conferences have effectively disappeared in the past year or two,
Speaker 3: that there are very few conferences actually still running. And
Speaker 3: now he should know because he's very involved in the
Speaker 3: in the conference scene. He does like thirty conference talks
Speaker 3: a year or maybe even more. He's really into it.
Speaker 3: So if he's saying that a lot of conferences are
Speaker 3: shutting down, I trust him about it, and it sounds
Speaker 3: really unfortunate to me. My information myself is anecdotal. I'm
Speaker 3: looking at services like sessionize, and it does seem to
Speaker 3: be the case that there are a lot fewer conferences
Speaker 3: out there. That's really unfortunate, and it seems to me
Speaker 3: that we should be supporting the conferences that are still
Speaker 3: running by participating by buying tickets, and attending by sponsoring.
Speaker 3: So if you can help a conference organizer keep the
Speaker 3: conference up and running, please do my last pick since
Speaker 3: I mentioned Tagous, Tajos has his own podcast these days.
Speaker 3: 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
Speaker 2: right there.
Speaker 3: And he recently posted a new episode which is really interesting.
Speaker 3: Usually most of his episodes are about he interviews people
Speaker 3: you know in a same similar vein to the way
Speaker 3: that we do. And and you know, even though we
Speaker 3: have a podcast, he has a podcast. I have no
Speaker 3: problems saying that his podcast is awesome. I highly recommend
Speaker 3: listening to it. In fact, I was a guest on
Speaker 3: his podcast a while back. But the reason that I'm
Speaker 3: mentioning it is that his latest episode, that he basically
Speaker 3: put out a week ago, is not an interview. Rather,
Speaker 3: it stages himself speaking for one and a half hours
Speaker 3: about the state of AI in twenty twenty five. He also,
Speaker 3: you know we mentioned MCP. He talks about MCP in detail.
Speaker 3: He talks about agents, he talks about RAG and much more.
Speaker 3: And I'm about halfway in. It's excellent. I highly recommend
Speaker 3: listening to that episode. I hope we can get them
Speaker 3: on our show as well, but regardless, I recommend listening
Speaker 3: to that episode to get really up to speed about
Speaker 3: the technicalities of AI in twenty twenty five. And those
Speaker 3: 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
Speaker 1: that are going to be converting code basis to typescript
Speaker 1: and Dan, I can people follow you on Twitter if
Speaker 1: they want to get info or do your Twitter or
Speaker 1: where can they find out if you publish this written
Speaker 1: form somewhere?
Speaker 3: Well if if and when I do, I'll obviously announce
Speaker 3: it on X, so yeah they can, and if they,
Speaker 3: if they're on X, please do follow me. I'm Dan Shapier.
Speaker 3: That's d A N S H A P P I
Speaker 3: R on X. I'm also on Blue Sky if you
Speaker 3: prefer that platform instead, so I'm there as well. On
Speaker 3: Blue Sky, I'm also Dan Shapiir, so find me there
Speaker 3: as well. Follow me here or there or anywhere. I'll
Speaker 3: gladly follow you back. Also glad to get feedback about
Speaker 3: our podcast in general, So feel free to contact me
Speaker 3: and that's.
Speaker 1: It, alrighty, And if you want to get a dad
Speaker 1: joke today, if you're amazed the quality of my curated
Speaker 1: dad jokes, you can follow me on x slash Twitter
Speaker 1: at Wonder nine to five. All right, with that, we
Speaker 1: will wrap this poppy up, thanks for listening, and we'll
Speaker 1: talk it to you next time.
Speaker 3: Bye,