Vue 3 has been rebuilt from the ground up using TypeScript. Since Vue2, Vue has supported TypeScript and still does. However, these days, I sense a certain hype and interest to develop Vue 3 apps using TypeScript.
Without any doubt, developing JavaScript apps using TypeScript is a life saver. TypeScript, in addition to the many goodies it brings, provides type safety. You will be happy, your IDE will be happy, and of course, it makes writing JavaScript more interesting and robust.
This article will take you on a step-by-step guide to creating a Vue 2 app, adding TypeScript support, upgrading the app to Vue 3 RC 5, compiling the app, fixing some common compile time errors, and finally make sure the app runs in the browser.
If you’re new to TypeScript, I suggest you watch this free course Learn TypeScript from Scratch by the distinguished Maximilian Schwarzmüller.
If, on the other hand, you want the source code accompanying this article, feel free to access it at this GitHub Repo.
Disclaimer - This article applies to existing Vue 2 apps that you want to upgrade to Vue 3 and add TypeScript to the mix, and to any new app.
Demo
Let’s start by creating a Vue 2 app, and going through the necessary steps.
Create a New App
As the sub heading suggests, we will be creating a Vue 2 app using the Vue CLI.
Step 1: Install @vue/cli NPM package
Open a terminal window and run the command:
npm install -g @vue/cli
Step 2: Create a new Vue 2 app
vue create vue-ts
Select the default preset
when prompted. The CLI scaffolds the files for you, and also initializes a Git repository for the app.
Step 3: Run the app
Run the app to make sure it’s working properly:
cd vue-ts
npm run serve
You should have a running app now!
Add TypeScript Support
Let's run the following command to add TypeScript support to our app.
vue add typescript
The command above downloads and installs the @vue/cli-plugin-typescript plugin to bring TypeScript support to our app.
Once installed, you are asked a few questions that are shown in Figure 1.
Installation Questions |
---|
Use class-style component syntax? Yes |
Use Babel alongside TypeScript? Yes |
Convert all .js files to .ts? Yes |
Allow .js files to be compiled? Yes |
Skip type checking of all declaration files? Yes |
Among the many things the plugin adds to the app, I’d like to point out the tsconfig.json
file at the root of the app folder. TypeScript uses this file to customize the compilation process, and gives you a chance to do so, to suit your needs. The settings below are recommended by the Vue team. However, feel free to add or change things as you see fit.
For a complete list of all the options available for you to use in this file, follow this link tsconfig.json.
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}
Finally, make sure to commit your changes to Git before moving on any further.
Upgrade App to Vue 3
Now that the app supports writing Vue Components in TypeScript syntax, let's move on, and upgrade the app to Vue 3.
Step 1: Add the Vue-Next Plugin
Open a terminal window and navigate to the root folder of our app. Then, run the following command:
vue add vue-next
The command downloads and installs the vue-cli-plugin-vue-next plugin to upgrade the app to Vue 3.
Figure 2 shows all the steps the CLI has taken to perform the upgrade.
Open the /package.json
file, and make sure the dependencies
and dev-dependencies
sections are similar to the one below:
"dependencies": {
"core-js": "^3.6.5",
"vue": "^3.0.0-rc.5"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.4.0",
"@vue/cli-plugin-eslint": "~4.4.0",
"@vue/cli-service": "~4.4.0",
"@vue/compiler-sfc": "^3.0.0-rc.5",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^7.0.0-alpha.0",
"vue-cli-plugin-vue-next": "~0.1.3"
},
The vue-next plugin automatically goes through your app files, and converts them to be compatible with Vue 3 syntax.
Step 2: Fix Warnings & Errors
At this point, if you compile the app, you will see some warnings and errors. These are mostly related to using TypeScript in Vue 3. Let's tackle these one by one.
Run the following command to compile and start the app in the browser:
npm run serve
The output is shown below:
Warnings
warning in ./node_modules/vue-class-component/dist/vue-class-component.esm.js
"export 'default' (imported as 'Vue') was not found in 'vue'
warning in ./node_modules/vue-class-component/dist/vue-class-component.esm.js
"export 'default' (imported as 'Vue') was not found in 'vue'
warning in ./node_modules/vue-class-component/dist/vue-class-component.esm.js
"export 'default' (imported as 'Vue') was not found in 'vue'
warning in ./node_modules/vue-class-component/dist/vue-class-component.esm.js
"export 'default' (imported as 'Vue') was not found in 'vue'
warning in ./node_modules/vue-property-decorator/lib/vue-property-decorator.js
"export 'default' (reexported as 'Vue') was not found in 'vue'
Errors
TS1238: Unable to resolve signature of class decorator when called as an expression.
Type '<VC extends VueClass<any>>(target: VC) => VC' is not assignable to type 'typeof HelloWorld'.
Type '<VC extends VueClass<any>>(target: VC) => VC' provides no match for the signature 'new (): HelloWorld'.
33 | import { Component, Prop, Vue } from 'vue-property-decorator';
34 |
> 35 | @Component
| ^^^^^^^^^^
36 | export default class HelloWorld extends Vue {
37 | @Prop() private msg!: string;
38 | }
ERROR in src/components/HelloWorld.vue:36:41
TS2507: Type 'typeof import("/Users/bhaidar/projects/vue-ts/node_modules/vue/dist/vue")' is not a constructor function type.
34 |
35 | @Component
> 36 | export default class HelloWorld extends Vue {
| ^^^
37 | @Prop() private msg!: string;
38 | }
39 | </script>
The warnings and errors show that TypeScript is not able to digest the HelloWorld
Component that's written in the old TypeScript syntax for Vue 2. Let's refactor this component to use the latest TypeScript syntax in Vue 3.
Currently, the /components/HelloWorld.vue
component's script block is defined as follows:
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
@Component
export default class HelloWorld extends Vue {
@Prop() private msg!: string;
}
</script>
First of all, notice the use of a lang="ts"
attribute on the <script>
element. This defines a TypeScript code block instead of a JavaScript code block.
Let's replace this code with:
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'HelloWorld',
props: {
msg: {
type: String,
required: false,
},
},
});
</script>
The code makes use of the new defineComponent
global method. This method lets TypeScript properly infer types inside of the Vue Component options.
The defineComponent
method accepts an input parameter object. It can be an Options API object or a Composition API object.
You can read more about Options API and Composition API by checking my article on Vue 3 Composition API, do you really need it?
Now that the HelloWorld
component is refactored. Let’s move on, and refactor the App.vue
component to use the new TypeScript syntax in Vue 3:
<script lang="ts">
import { defineComponent } from 'vue';
import HelloWorld from '@/components/HelloWorld.vue'
export default defineComponent({
name: 'App',
components: {
HelloWorld
},
});
</script>
Let's run the app by issuing the following command:
npm run serve
Wow! More errors and warnings!
Argument of type 'typeof import(\"/.../vue-ts/
node_modules/vue/dist/vue\")' is not assignable to parameters of type 'PublicAPIComponent'.
Type 'typeof import(\"/.../vue-ts/node_modules/vue/dist/vue\")' is not
assignable to type 'ComponentOptionsWithObjectProps<any, any, any,
any, any, ComponentOptionsMixin, ComponentOptionsMixin, EmitsOptions,
string, Readonly<{ [x: string]: any; }> | Readonly<...>>'.
...
__ Importing Vue Components__
The error above is generated for Line #2 in the main.ts
file:
import { createApp } from 'vue';
import App from './App.vue'
^^^
createApp(App).mount('#app')
Typescript, like ES 6, supports modules by using the keywords import and export. As long as you are writing your modules in TypeScript, that is, module files ending with .ts then you are covered.
The error above signals that TypeScript is not able to import the App
component, as if the App.vue
module returned is not understood by TypeScript.
The solution? Define a shim or declation file at the root folder of the app. This file has the extension of .d.ts
. It basically makes it easier for the tooling to know how to handle *.vue
files, also known as Single File Components (SFC).
Locate the shim-vue.d.ts
file at the root folder of your app and replace its content with the following:
declare module "*.vue" {
import { defineComponent } from "vue";
const component: ReturnType<typeof defineComponent>;
export default component;
}
The code declares a TypeScript module for every file ending with *.vue
(SFC).
TypeScript will digest such a module as having the following:
- An import statement for the
defineComponent
global method - Declaration of a
component
variable of typedefineComponent
- Finally, a default export of the
component
variable
Having the shim file above allows TypeScript to consider any Vue Component to be a well-defined module.
Run the app
Now you can safely run the app knowing that it will run successfully inside the browser.
Run the following command to start the app:
npm run serve
Navigate to the URL http://localhost:8080
inside your browser and you should be able to see the default home page of a newly created Vue 3 app using TypeScript, as shown in Figure 3 below.
Use Composition API with TypeScript
Now that the application is TypeScript-aware, let me show you how you can make use of the Composition API and type safety to define a function with typed parameters.
Replace the content of the HelloWorld
component script with the following:
import { defineComponent } from 'vue';
export default defineComponent({
name: 'HelloWorld',
props: {
msg: {
type: String,
required: false,
},
},
setup(props) {
const printMsg = (msg: string) => console.log(`The message is: ${msg}`);
return {
printMsg,
};
},
});
Notice how the code defines a new function using the Composition API with a typed parameter. The parameter is named msg
and has a data type of string
.
Conclusion
Whether you are upgrading your existing Vue 2 app, or starting a new one, the article provides a step-by-step approach to adding TypeScript to the app, upgrading to Vue 3, fixing all warnings and errors, and finally running the app successfully in the browser.
This is a stepping stone in your way to build Vue 3 apps with TypeScript. The next step is to familiarize yourself more with TypeScript.
Happy Vueing!