Vue has a somewhat mixed TS story. On the other hand most notable packages support it well but then there are also large painpoints like Vuex that can make your life miserable. Upcoming Vue 3 is completely rewritten in TypeScript which will improve TypeScript support hugely. In attempt to future-proof a rewrite of a large existing Vue app, I decided to start gradually typing it with TypeScript. Here are some of the initial notes from my experience.
(Disclaimer: even though I’m an experienced Vue developer, I’m a complete TS-newbie so YMMV.)
The Unbearable Lightness of Starting Slow
Problem with most TypeScript tutorials and simple examples is that they expect you to either start from scratch or completely convert all of your code to TypeScript from the very first step. For an existing codebase, that’s obviously not going to happen. I would also personally advice against it even if there were tons of tests and all the time in the world to do it, especially if you’re new to TypeScript.
The great secret of TypeScript is that you can start as small as you like and add typings gradually. By doing it this way you’ll probably find some problems with your existing code and get the chance to refactor it properly as you move forward. Granted, you won’t get the full benefits of TypeScript before you reach a certain critical point of typing completeness, but starting it small will cost you almost nothing so its very easy to get started. Life with legacy codebases is all about compromises and this path will at least give you a chance for better future.
Step 1: Initial Installation
If you’re vue project is created with vue-cli, adding typescript looks easy: just
vue add typescript. Problem is, the defaults may not be that good for you and you should know what they mean. Here’s what I answered to the installer questions:
Use class-style component syntax? ❌No
By default this plugin suggests you to use class-style components which are an easy way to get started with Vue and TypeScript. Class based api was supposed to be the future of Vue, but the class syntax is in many ways problematic and if you plan to upgrate to Vue 3, you will want to consider composition api instead.
Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? ✓ Yes
If you have an existing project where you use Babel, you’ll definitely want to keep using it with TypeScript as well.
Convert all .js files to .ts? ❌No
Allow .js files to be compiled? ❌No
lang="ts" will be compiled, and that’s the way you’ll want it to be when starting gradually.
Step 2: Cleaning Up
The “helpful” installer will modify your existing code so you’ll need to do a bit of cleanup before getting back to business. First, you’ll want to delete the re-appeared
src/components/HelloWorld.vue as I’m quite sure no one is actually using that in a real project (right??). You also want to fix the havoc in your
src/App.vue while you’re at it. The
main.js is now renamed to
The newly added
tsconfig.json includes somewhat strict settings that may or may not work with your project. The stricter the rules, the more benefits you get from TypeScript, but on the other hand the stricter the rules, the harder it is to make your existing code valid. For me one of the first steps with an existing project was to disable strict checking. This allowed me to gradually add typings to complex files without spending hours and hours in debugging or adding the file full of ignores. Note that by loosening the checking you will lose some of the most powerful gains you could have from your editor and TS in general so evaluate yourself if you can leave it on or not.
(For example, with strict checking on, when editing a method inside a Vue component, the editor will know that
this refers to a specific Vue instance. This is super powerful when it works. When strict checking is disabled,
this inside of a component method is inferred as
any type which will not help you at all.)
I would not tweak tsconfig too much more before you understand better the challenges in your specific project. Give yourself time to test and experiment before committing to any specific rule.
Step 3: Start Adding Typings
Now you are ready to start adding your first types! I’d suggest to start in a file that is large or otherwise not so easy to read at a first glance — this is where you’ll get most gains.
Oh yes, Vuex. Have a plan for Vuex. Almost all larger projects use Vuex and for example the
mapState helper, so you’ll immediately be in a world of trouble as Vuex does not have proper TypeScript support and no simple solution has been found by the community. This does not mean that you can’t use Vuex with TypeScript or that there are no solutions. It just means that there are no good universal solutions. You need to experiment and evaluate which of the dozens of proposed workarounds works best for you. And know that if it at some point stops working in your specific edge case, you’re alone with your problem as there is no officially supported way. This obviously sucks big time, but there is at least some light at the end of this tunnel as Vue 3 will eventually bring proper TypeScript support to Vuex as well.
Try not to ignore code. Alarm bells should ring if your TypeScript code starts gathering
// @ts-ignore-lines more than actual refactoring. It’s the same with linting: the point is to make your code better, not to work around your tooling. Sure, there are situations where practicality beats purity but make a mental note for yourself to keep those situations exceptions. If you find a file too hard to convert, just leave it be until you or your tools become smarter.
.js files and if you have tools that generate new files, convert them to generate
Secondly, try not to change too much in one go. It’s very easy to stumble down into the rabbit hole and use hours and hours adding typings with very little practical gains. As you learn more and as Vue world matures, you’ll be getting more and more out of TypeScript. And then some day when you understand more about your project and the tools, you’ll see more clearly when it is the right time to take the last step of converting all the remaining code to TypeScript. Remember, cleaning up your codebase is not a race, it’s a neverending journey.
Things Will Break — It’s a Good Thing
Legacy code is difficult in many ways and hunting down new bugs that previously weren’t there can feel frustrating. But remember that this is exactly why you want to use TypeScript in the first place: to see and fix those bugs early. It’s very likely that your old production code is relying on a bug you didn’t even know was there and turning on TypeScript will now break that code. Learn to embrace these growing pains because fixing them will make your code better and keep your productivity high in the long run.