How to use Vue with TypeScript
The Secret Instructions that the Official Docs won't tell you.
First, read the relevant parts of the official docs. Following their instructions for configuring tsconfig.json
, <script setup lang=ts>
, etc. will get you like 99% of the way there.
This article is going to look at some of the gaps that are missing from the official documentation.
Disclaimer. I assume that you, like me, are not following their official recommendation to use create-vue. Maybe that would solve all of the problems outlined in this article. But I generally don’t like using pre-built boilerplate, if I can avoid it. I prefer building my own config files so I can understand how everything in my project works.
Table of Contents:
- 1. Deal with aliases
- 2. Run type-checking with pre-commit
- 3. Defining type declartions for Vue components
- Further Reading
1. Deal with aliases
The official docs do address this topic, but I want to make it more explicit.
Let’s say you make an alias within your vite.config.js
.
This is great. Because now instead of having to import my files with relative paths
import Thing from "../../thing.ts"
😕
or super long absolute paths
import Thing from "django_vue_experiments/vite_assets/thing.ts"
😰
we can instead use an alias
import Thing from "@/thing.ts"
⭐
But there is one more step. Vite knows about our alias, but the TypeScript compiler does not.
In your tsconfig.js
you need to add your alias to compilerOptions.path
:
Now TypeScript will know how to handle imports from your '@'
alias.
2. Run type-checking with pre-commit
When building for production, Vite will apply type-checking and let you know if there are any errors with your TypeScript compilation.
But before that happens, it’s definitely a good idea to also run type-checking as part of your pre-commit git hooks.
To do this, I added a check
npm script that runs type-checking (without emitting the compiled JavaScript).
Because I read the official docs, I knew to use vue-tsc
rather than TypeScript’s typical tsc
package. This enables us to apply TypeScript type-checking to .vue
files as well as .ts
files.
Now we make a hook in our .pre-commit-config.yaml
to run our check
script whenever there are new changes to a .ts
, .tsx
, or .vue
file.
Voila. Now pre-commit will run type-checking with every git commit.
Note that we must set pass_filenames: false
otherwise our vue-tsc --noEmit
command will only target the specific files that have changed. This is bad! That would exclude our own *.d.ts
type definition files and every type definition file within our node_modules. That’ll cause your type checking to fail with the error: Cannot find module ... or its corresponding type declarations.
.
3. Defining type declartions for Vue components
Following the instructions in the docs allows you to use TypeScript within Vue SFC component scripts. But it doesn’t appear to give types to that SFC component itself. When you import your TypeScript-enabled SFC into a .ts
file or another .vue
file that has lang="ts"
enabled, your IDE won’t recognize their types and the TypeScript compiler will complain. When you run your vue-tsc --noEmit
you’ll run into an error like this:
The current standard for fixing this is sort of a catch-all solution. I have no idea where it originated, but it’s been co-signed by several stackoverflow answers, a Digital Ocean guide, and every real demo that I’ve come across.
In your src
directory, you need to add this shim:
This tells TypeScript that every vue component (exported from a "*.vue"
file) has a type of DefineComponent<{}, {}, any>
. This approach works, we can now import vue components into .ts
files, but there are some problems with it.
It’s not terribly strict. Because the {}
and any
types are too loose we need to disable eslint checking on this declaration (Shout out to this real world demo). But most importantly, we lose the ability to get specific typing on each of our components. We can’t distinguish one component from another, and we can’t see the types for their props.
Prospective solutions:
- Write your SFC script code within a
.ts
file. - Install the Volar VSCode plugin. I can see how that could help VSCode type-checking, but how would it fix the actual
vue-tsc
parser? See: Vue docs, this stackoverflow answer, this github issue. - Manually write out type declarations for every Vue component in a
.d.ts
file? Blehh…
Again, I’d love to hear from you if you’ve found a solution that works better.
I hope this MVP configuration was helpful. There’s definitely the potential to make the Vue component typing stronger, I’ll update this page as I learn more.
🔆
Further Reading
- How to use eslint with Vue and TypeScript: Check out the next article if you to see the rest of the eslint configs that I use to get Vue to work with TypeScript.