The Issue of Halfway Committing To Typescript as a Team
- Published on
- Updated on
It's generally been said that Typescript is helpful when codebases are large and complex, or when a team of people are contributing to them. A quick way for Typescript to be detrimental to a project is when the team only partially commits to it.
This applies to both libraries and applications, but library authors should be even more cognizant of embracing good Typescript practices - after all the point of libraries is for others to use them whereas users of an application don't really care if the types and interfaces are correct. In both cases, type safety can make systems more robust and easier to use.
What does Halfway Committing To Typescript Mean?
In no specific order, here are some behaviors that would qualify as halfway committing:
Using the
any
type. Usingany
is not necessarily a bad thing for quickly prototyping a new feature or function, but for moving to production it just shouldn't be acceptable. This should be a slap on the wrist if they slip into a protected branch, or better yet should be corrected in a merge request.Using
@ts-ignore
and@ts-nocheck
. The one case where these are useful and acceptable are when porting over a large untyped file, but again shouldn't be merged into a protected branch or production. Major slap on the wrist if these make it into production.Adding new Javascript files. Maybe the project is in the process of migrating from Javascript to Typescript in which case the codebase may still have some Javascript files that need to be converted. But if the team has made a commitment to Typescript, why prolong the migration by adding more Javascript?
Not using strict compiler settings. By not using strict compiler options, the project and the team is losing all of the benefits of using Typescript.
Why does Halfway Committing To Typescript Happen?
There are common reasons for why this happens:
Deadlines. Business expects that features get shipped on time and provide value. This pressure may be heavy on the team so corners may get cut to relieve that pressure.
Previous patterns are followed. This is one of the main problems with allowing
any
types to slip into shared branches. New developers in particular tend to pick up and follow behaviors of the team, so bad Typescript habits multiply.Lack of proficiency. It may be the case that developers on the team need time to learn and practice Typescript.
Laziness. Perhaps members of the team just want to get the job done in the easiest way possible and don't want to spend the time and effort.
The Problems That Halfway Committing To Typescript Causes
Any tool, technology, or new library is not a silver bullet, there are always tradeoffs and this is true for Typescript.
Here are some of the tradeoffs.
Benefits:
Type safety. Typescript is a tool that helps you to enforce type safety, that is its primary job. Those hundreds, thousands, or even millions of tiny edge cases are simply not possible for a human to catch. Typescript is good at catching these and will likely help you sleep better at night.
In-editor documentation. This one is particularly helpful for consumers of libraries. Autocompletion of arguments and return types is one of the best features of Typescript and is a huge time-saver. Most developers either don't like to, or don't have time to manually update documentation in parallel, this is a great way to solve that problem. Especially for large data interfaces this is key.
Code quality. This goes hand-in-hand with Type safety but deserves it's own mention. One can absolutely write bad code in any language, but the practice of being intentional about data types makes one slow down and understand what the implementation is doing.
Clear I/O interfaces. The contract of data going in and data coming out of a system is important and always helpful to the user. This is the whole reason why things like USB ports and Mac lightning ports are useful, users of the adaptors can use the same interface to communicate with their devices. This should be the same case with software systems.
Short feedback loop. Iterative development relies on fast feedback loops. The Typescript compiler is great at this - it will throw red squiggles when it flags an issue. Better to catch these things as fast as possible as opposed to some issue going out into production and having to wait for the next release to get the fix out.
Sacrifices:
More code. There will inevitably be more code in a Typescript project when compared to one with Javascript, and especially in libraries there can be thousands of lines for types and interfaces alone. More code is more maintenance and complexity.
Longer build times. In most cases an additional compilation step is added to the build process, especially for applications that are running in a browser. Depending on the size of the codebase, build times could be much longer. There are however many tools nowadays that speed up builds, like
esbuild
andswc
.Learning curve. For developers coming from a Javascript background, they may be unfamiliar with static typing. For those coming from Java, Go, or other typed languages, the learning curve may be smaller. It costs time and money for developers to learn how to use Typescript.
Initial setup cost. It's not always straightforward to get Typescript set up. It's also not always easy to get Typescript to work with a particular toolchain, and some tools may require a lot of configuration. Overtime this seems to be getting easier as adoption grows with projects like ts-node, deno, bun.js where Typescript is a first-class citizen, but this one shouldn't be overlooked.
By only halfway committing, the team may be sacrificing all of the benefits while also accepting the tradeoffs. If those few bad behaviors mentioned earlier continue to happen, the codebase will likely get much larger and more complex, build times will get worse, and even more code will be untyped and not any safer. Every time a developer introduces another any
type into the codebase, you can kiss goodbye to type safety, in-editor documentation, any compiler warnings, and that sweet I/O interface. Overtime this has major consequences for the health of a project.
In other terms, this behavior is like buying an expensive private jet and never using it, but paying for all the overhead like fueling it and paying for the insurance.
It's probably better to not start using Typescript until the team is fully aligned and ready to commit to avoid the above problems. Don't invest in the private jet if you aren't gonna use it.
Quick tip:
One way to avoid this bad behavior is to write up a team contract or a set of guidelines that people can follow and include it as part of a merge request checklist. If that's not enough, you can use es-lint to enforce the rules (no-explicit-any rule)[https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-explicit-any.md].