Seamlessly migrating from JavaScript to TypeScript: A Practical Note

Sahin Deniz
Insider Engineering
8 min readOct 2, 2023
Credit goes to dear Ozlem Guder

Introduction

In this article, we’ll take a laid-back yet insightful journey into migrating from JavaScript to TypeScript for a front-end application without causing chaos in your development team. Remember, it’s all about striking the right balance between flexibility and type safety.

The Quest for TypeScript

So, why embark on this adventure?
It’s as simple as it gets — it’s all about safety and the magical world of autocompletion 😋.

TypeScript is like JavaScript’s sophisticated cousin; it’s still JavaScript code but with an added layer of protection.
Some folks may argue, “Just write tests for your code, and you’re golden!” But wait, how do you achieve that delightful auto-completion and rock-solid type safety without TypeScript? Spoiler alert: you can’t.

Let’s face it; TypeScript brings unparalleled type-safety and autocompletion to the table. Even JSDoc, with all its goodwill, can’t cover all the advanced cases you’ll stumble upon in your coding journey.

The Pros and Cons

Before we dive deeper, let’s consider the pros and cons of migrating to TypeScript.

While TypeScript offers enhanced type safety and autocompletion, it’s essential to acknowledge potential challenges such as a learning curve, initial setup, and the need to maintain type definitions.
Understanding these aspects will help you make an informed decision.

The Grand Plan

Now, let’s dive into the nitty-gritty of planning this migration. Just remember, perfection is not the goal here; it’s all about smart planning and execution.

As I researched Vue 3, I couldn’t help but gather some intel on TypeScript.

After all, it was inevitable that we’d face this tech debt eventually.

So, the first question that popped up was;

“Can we mingle TypeScript and JavaScript harmoniously?”
Absolutely!

Fear not, my friends; by tweaking your tsconfig.json file with allowJS magic, you can make TypeScript and JavaScript coexist peacefully in your codebase. You can temporarily say goodbye to type-related woes and errors, all while building your code with confidence. If you want your editor to check types for your JS code, you can enable checkJS and disable strict mode for allowing any types on your code.

// tsconfig.json
{
"compilerOptions": {
"allowJs": true,
"checkJs": true,
// Other TypeScript options
},
// Rest of your configuration
}

This basic configuration enables TypeScript and JavaScript to work together seamlessly.

Want more details? Check out the documents:

Now, for our second question: “Can we set up a CI check to weed out code with type issues?” Yes, you can! tsc, a library that can be your trusty CI gatekeeper. Just trigger it with a simple NPM script command.

Migration Strategy

Armed with answers to our planning questions, it’s time to draft a migration strategy. You’ve got two options here:

Migrate the whole project at once:
This means converting all your .js files to .ts (or .jsx to .tsx and so on) all at once. You’ll need to configure your bundler to compile TypeScript and brace yourself for any missing type-related headaches. This approach is doable, but it does require freezing your project and development cycle until the migration is complete.

Migrate the project part by part:
This is my recommendation — take it slow and steady. Divide your files into manageable chunks and migrate them part by part. This way, you won’t halt/freeze the entire application’s development progress, just like we did in our team.

In due time, you can sketch out how many files to migrate in each development sprint and how many developers can join the TypeScript migration party.

The Glitter and Grind

When you start the migration process, you’re likely to uncover a few pesky bugs lurking in your code. No biggie — mark them for later. Your primary focus should be crafting meticulous type definitions and interfaces, all while connecting them in the right order.

Admittedly, your application code may have evolved in a way that’s not the best fit for TypeScript. You might need to introduce alternative logic to your code to address this issue or create extra tasks before you migrate.

The Knowledge Quest

Sure, not everyone on your team might be fluent in TypeScript. But don’t let that hold you back. All you need is a TypeScript maestro to lead the migration charge, but of course more the merrier.

As developers, we’re constantly adapting to new technologies and stacks. TypeScript is no exception. It’s a fantastic tech stack that’s easy to learn through online workshops.

For starters, I recommend the “Total TypeScript” tutorial series by Matt Pocock. He’s an excellent teacher with a treasure trove of videos and exercises that are a must-learn. Dive into the world of TypeScript:

Matt also offers advanced tutorials for those looking to up their TypeScript game:

Challenges and Best Practices During Migration

Migrating from JavaScript to TypeScript can be a smooth transition, but it’s not without its challenges. Here are some common hurdles you might encounter and best practices to overcome them:

1. Learning TypeScript

Challenge: Developers who are new to TypeScript might face a learning curve.
Best Practice: Encourage your team to invest time in learning TypeScript through online courses, workshops, or books. Providing resources and mentorship can expedite the learning process.

2. Type Definitions

Challenge: Creating accurate type definitions for existing JavaScript code can be time-consuming.
Best Practice: Start by defining types for critical components and gradually work your way through the codebase. Consider using tools like tsc to help identify type issues automatically.

3. Legacy Code Compatibility

Challenge: Older code written in JavaScript might align differently with TypeScript. In other words, you may bump into an antipattern case.
Best Practice: Use TypeScript’s any type as a temporary solution while maintaining a plan to gradually improve type safety over time. Setting up linting rules can help enforce TypeScript conventions.

4. Testing and QA

Challenge: Ensuring that your application behaves as expected after the migration can be challenging.
Best Practice: Invest in thorough testing, including unit tests and end-to-end tests. Automated testing can catch regressions and ensure the reliability of your codebase.

5. Continuous Integration (CI)

Challenge: Implementing a CI pipeline to catch type issues can be complex.
Best Practice: Leverage CI tools like GitHub Actions, Travis CI, or Jenkins to automatically run TypeScript checks. Make this a part of your development workflow to catch type-related issues early.

6. Documentation and Knowledge Sharing

Challenge: Maintaining up-to-date documentation and sharing knowledge about TypeScript within your team.
Best Practice: Keep comprehensive documentation on type conventions, project setup, and migration guidelines. Encourage team members to share their TypeScript experiences and insights regularly.

By addressing these challenges with the recommended best practices, you can navigate the migration process more effectively and ensure a successful transition from JavaScript to TypeScript.

Choosing the Right Bundler

Remember the bundler part of my previous article about Vue 3 migration?

If you’re using Vite, you’re in luck because it supports TypeScript out of the box.

But if you’re starting fresh or have a smaller project, consider Bun — a new JS runtime that’s gaining traction:

As for Webpack and other bundlers, you might need to do some detective work but trust me, there’s a solution out there for you. And if all else fails, you can always consider leaping Vite or Bun!

The Bug Reduction Advantage

One of the most compelling reasons to migrate from JavaScript to TypeScript is the remarkable reduction in bugs and runtime errors. While it’s challenging to provide precise figures, many development teams have experienced a significant decrease in the number of bugs after adopting TypeScript.

Type Safety’s Role

The secret sauce behind this bug reduction lies in TypeScript’s type system. By explicitly defining types and catching type-related errors during compile time, TypeScript helps developers identify and rectify issues before they make their way into production code.

A Few Key Benefits

Here are some key benefits that contribute to bug reduction:

  1. Early Detection: TypeScript’s static type checking identifies potential issues before runtime. This means you catch errors during development, reducing the chances of bugs slipping into your codebase.
  2. Improved Code Quality: TypeScript encourages developers to write more robust and self-explanatory code. Strongly typed code is less prone to logical errors, making your application more reliable.
  3. Refactoring Confidence: With TypeScript, refactoring becomes less daunting. When you change a piece of code, TypeScript flags any type-related problems, allowing you to refactor with confidence.
  4. Documentation as Code: TypeScript type annotations serve as living documentation for your code. They provide valuable insights into function signatures, expected input/output, and code contracts, making it easier for developers to understand and use your code correctly.

Real-world Examples

While the bug reduction percentages can vary depending on factors like project size and complexity, several case studies and anecdotal reports highlight substantial improvements:

Slack: Slack’s transition to TypeScript significantly decreased runtime errors, contributing to a more stable and reliable platform.

Microsoft: As one of the early adopters of TypeScript, Microsoft experienced a remarkable reduction in production bugs across various projects, including Visual Studio Code.

The Angular Framework: After migrating from JavaScript to TypeScript, the Angular team reported a 50% reduction in bugs during development and a 90% reduction in bugs encountered by end-users.

The Bottom Line

While it’s challenging to predict the exact bug reduction for your specific project, the bug-fighting capabilities of TypeScript are well-documented. By embracing TypeScript, you not only enhance your codebase’s reliability but also reduce the time and effort spent on debugging and maintenance.

Remember, the true value of TypeScript lies not just in bug reduction but also in creating a more maintainable and collaborative development environment.

Still Unsure?

If you’re craving more migration stories and juicy details, scour the web — there’s no shortage of resources. My recommendation? Join TypeScript and developer communities on Discord. You’ll find fellow adventurers who can share insights, benchmarks, and tales of bug reductions after making the TypeScript leap.

Here are some Discord communities worth exploring:

Conclusion

So there you have it — migrating from JavaScript to TypeScript, in a cool and practical way. Embrace the power of TypeScript, take it one step at a time, and remember, you’re not alone on this journey. Happy coding! 🚀😎

Bonus: DeviceScript — TypeScript for Microcontrollers

DeviceScript
DeviceScript

Discover DeviceScript, a revolutionary fusion of TypeScript and microcontroller development. It brings TypeScript’s familiar syntax to low-resource microcontrollers, offering small-footprint bytecode, hardware abstraction, debugging, and a rich package ecosystem. Explore this innovative technology for your embedded projects here.

Connect with Us and Explore More

Are you intrigued by the art of crafting and unraveling the mysteries of front-end design systems? Look no further than “How To ‘Design’ a Design System — Part I” by the exceptionally skilled Alp Şenel, Senior Front-End Developer at Insider.
This captivating read is a key to understanding the inner workings of design systems in front-end applications. Don’t miss it!

Also, don’t forget to follow the Insider Engineering Blog for more insightful articles. ✍️

You can reach me on LinkedIn 📥

--

--