6 ms·
How to Do a TypeScript Conversion
- BrianHenryIE 3y agoIs there any automated tool that can add types? I have Playwright E2E tests – it seems reasonable that the type each variable is used as could be recorded and the code annotated with that.
- chriskrycho 3y agoThe specific thing you’re asking for there is possible but non-trivial if you try to do it at runtime with tools like Playwright; more importantly it’s more overhead than you really need, and runtime types are often not exactly the same as what you want to describe an API, as you’ll be looking at various objects where you then have to decide how much of the prototype chain to walk and lots of other decisions like that. The way most tools in the space work is by leaning on TS’ own programmatic API. The two most interesting such tools I know of are AirBnB’s ts-migrate and LinkedIn’s RehearsalJS (full disclosure: I was loosely involved in design discussions for the matter, but did not contribute to it code-wise).
- idontwantthis 3y agoI wouldn’t recommend that. You want your types to have meaning for the business and you want to derive types from each other. It’s counterproductive to individually type every variable without context.
- rocqua 3y agoIn python at least, with strict-mode, Any is useless because you can do almost nothing to it. You can print, you can call 'dir' 'help' and other built-ins, and you can pass it to functions that accept Any. That means that sprinkling any around your code isn't a great way to silence errors.
- idontwantthis 3y agoI don’t know about Python, but for converting JS to TS you start with allow-implicit-any turned on and add types to your business objects as you go. Eventually your turn it off and fix what remains.
- chriskrycho 3y agoNote that while a lot of people do it this way, my blog post this discussion is notionally about on expressly argues that that’s exactly what not to do!
- idontwantthis 3y agoI have now, in fact, RTFA! You definitely make a lot of good points.
- deleted 3y ago[deleted]
- Aeolun 3y agoHaving done several Typescript migration on 100k+ line codebases, this rings entirely true for me. An additional problem with doing partial migrations is that it leaves people in a position to just not fix the file they’re working on, since half converted files are the norm, and they have more important things to do.
- pyrolistical 3y agoI forget where I learned this. But this follows my refactoring legacy code principal. Your new code (typescript in this case) should never reference the old code. Old code is the only thing that knows about new code. The reason being is if your new code ever knows about your old code, your new code will carry on old code forever. You can never escape. But if only old code knows about new code. Your new code is perfectly clean. For this typescript conversion problem the nice thing is you can configure typescript to allowJs and your existing code can import compiled typescript
- quickthrower2 3y agoOoh that may have been the approach for my sister comment about circular deps. Thanks for the insight. The solution for me would be make new libraries (without circle) and migrate old code to reference them. Instead of pulling old code into the new libraries with the circular deps coming with them
- nicoburns 3y agoYou could also just remove the circular deps first?
- quickthrower2 3y agoYou could. Probably a lot of work! You are also fighting against new code going in adding more. Splitting into modules prevents this.
- redbar0n 3y agoBut then you effectively won’t be able to build on what you already have? Or, you’d have to continue expanding the old code using only the old language/methods (here: JS instead of TS).
- Smaug123 3y ago(The "strangler pattern" can encompass this if you make an exception to the policy for the facade that hides the legacy code.)
- andrewstuart 3y agoI think ChatGPT makes a huge difference here. I routinely ask it to convert javascript to typescript. It does a great job for the most part in seconds, leaving me to clean up where it got it wrong, and then fix the bugs that typescript exposed. Surely this makes the task easier? The only trap to avoid is it takes a conscious decision to do no other factoring than the typescript conversion. Don’t start “just fixing this other little thing while I’m here”.
- shepherdjerred 3y agoThe real benefit of a statically typed language, especially one like TypeScript with a rich type system, is that you get to express your application in terms of the type system, and that you tend to think a lot more about your data structures. You can, for example, make invalid states impossible to represent. This can catch a huge number of bugs and prevent some types of bugs from ever appearing. If you're using something like ChatGPT or some other automated system to add types to your code you have adopted a type system with receiving very little of the benefits. You end up with many of the drawbacks and only a little of the reward.
- joshxyz 3y agoalso when i edit one file, i can just run tsc --noEmit and i will see the list of affected files. smoothest workflow ever.
- rocqua 3y agoYou can do a refactoring later, or reap the benefits of typescript for all new code. ChatGPT here doesn't need to do a good job. You get value just by getting a codebase type-annotated.
- Smaug123 3y agoIt's sort of postponing the actual work, though: there is indeed value in asserting that your code adheres to some domain model, but it's not entirely clear to me whether the value is outweighed by the friction when that arbitrary ChatGPT-generated domain model disagrees with your domain. There is dramatically more value in asserting that your code adheres to the correct domain model, and you can only know that by, well, modelling your domain.
- esprehn 3y agoAirbnb converted many millions of lines gradually using automation and wrote about it: https://medium.com/airbnb-engineering/ts-migrate-a-tool-for-migrating-to-typescript-at-scale-cd23bfeb5cc https://medium.com/airbnb-engineering/ts-migrate-a-tool-for-... And also open sourced the tooling: https://github.com/airbnb/ts-migrate https://github.com/airbnb/ts-migrate
- bichiliad 3y agoBit of a shameless plug, but I wrote about how Etsy migrated to TypeScript a few years back. I agree with the author generally: TS strict mode is the way to go, and you may find yourself regretting the alternative, even if it sounds appealing. It takes a lot of work, but most of the effort ended up being: 1. writing types for the big, central libraries that everyone uses (and make them "smart enough" to infer as much as possible). 2. teaching everyone at the company a brand new language without them hating it, and without their team losing (much) velocity. https://codeascraft.com/2021/11/08/etsys-journey-to-typescript/ https://codeascraft.com/2021/11/08/etsys-journey-to-typescri...
- steve_taylor 3y agoI’m currently going through this at work and my experience even with smaller packages agrees with the author: After trying it the loose way and failing, I realised the best way to proceed is with maximum strictness from the start, converting the .js files to .ts in dependency order. When a file is converted from .js to .js, it’s almost guaranteed to be 100% converted. There are probably some edge cases where some refactoring necessitated by the conversion propagates to its already converted dependencies, but I would expect those to be minimal.
- appplication 3y agoI wish mypy was the same. I’ve tried to add typing for our fairly large (100k+ LOC) python libraries 3 or 4 times now and failed every time. One of the big issues is strict isn’t well supported, and some libraries and frameworks just do not play well with typing or have separate half baked stub libraries. I also firmly believe it should integrate seamlessly with e.g. pre-commit, and that hasn’t been my experience either, with special workarounds needed for the pre-commit hook to perform identically to running it in the locally installed venv. So we just write new code with types by convention but have no actual linting or checks because it’s too hard to actually static typing to an existing repository.
- IshKebab 3y agoIn my experience the best way is: 1. Give up on pre-commit. It would be nice if it just worked, but it doesn't. Just make a type checking script and run it as the first thing in CI. 2. Use Pyright, not Mypy. It's much much better. 3. You can start with a very loose config and gradually make it stricter. 4. Make liberal use of `type: ignore` (and add TODOs to make it clear they're not intentional). 5. You can also make an alias for Any and use that for types that you intend to figure out but haven't yet. Typing Python that wasn't written with type hints in the first place is always going to be a complete nightmare though. People that write untyped Python tend to write it in highly dynamic ways and Python's type hints often aren't expressive enough to describe them (e.g. they only very recently added support for properly typing kwargs).
- scotty79 3y agoI don't really like the idea of sprinking around 'any' to silence noImplicitAny problems. Either put in the correct type or leave it as is. Placing 'any' in the code just fossilizes badness. Implicit any is just easier to accept, because when you finally have time to get serious you can disable it an write the correct types. You don't have to hunt for 'any' throughout the code.
- triyambakam 3y agoI think it can make it easier to keep track of the work that needs to be done when explicit any (noImplicitAny) is used. I use them to monitor percentage of progress
- scotty79 3y agoHow do you measure how many any's you got in your code? How do you track where you have them? Just \bany\b regexp?
- eyelidlessness 3y agotypescript-eslint has a rule targeting explicit any annotations, and even several rules about assignments etc which are inferred as any. If you really want to include any in a strict-gradual migration strategy (I’m skeptical but could be persuaded depending on the baseline), you can use those rules as warnings or whatever makes the most sense. It’s definitely statically analyzable without a ton of bespoke effort.
- nicoburns 3y ago"rg -w any", with an optional --count if want to count them rather than list their locations. It functions quite similarly to "unsafe" in Rust, and like "unsafe" I think ideally each "any" would come with a comment explaining why it's use is justified in that case (which is hard to enforce if the "any"s are implicit)
- scotty79 3y ago
- brailsafe 3y agoThis seemed like the best approach, and the team I was last part of took it with reasonable positive results. One of the challenges however was less about actual module dependencies being converted before or after, but about conflict resolution when we'd end up needing to create and reconcile the same type across sibling leaves and coordinating that on the fly, especially while learning about the files we might not have written and only being able to reason about the logic at a high level before getting into the weeds of it.
- goda90 3y agoI recently converted most of my team's code alongside two coworkers. While I think it probably shortened the overall time, trying to split that work meant we stepped on each other's toes a lot. Due to the usual time pressure and regression avoidance, we've ended up with more casting and non-null assertions than I'm comfortable with. Still I think it was worthwhile.
- jakubmazanec 3y agoThe article only touches this: when converting to TypeScript, `any` is useful, but in the end you don't want this type in your codebase - so don't forget to use typescript-eslint [0] and turn on those no-unsafe-* rules which guard against `any` leaking into your code. [0] https://github.com/typescript-eslint/typescript-eslint https://github.com/typescript-eslint/typescript-eslint