Your first Vue 3 app using TypeScript

2 Part Series

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.

agregando-typescript


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.

agrega-vue-next

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 type defineComponent
  • 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.

vue3-typescript-pagina-inicio

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!

You might also like