Vue.js with TypeScript

Image

Nowadays a lot of projects get benefits from using TypeScript. It really helps to avoid stupid mistakes with passing wrong arguments, documents every method with types and helps you to write javascript almost without opening your browser.

I don't see a point of starting new projects without TypeScript nowadays. That's why in this article we will integrate TypeScript in fresh Vue application.

First of all we should generate new project with vue-cli.

vue init webpack foo

Our application was successfully installed.

Now we need to install ts-loader for transpiling ts files and typescript package itself.

yarn add typescript ts-loader@3.5.0

We used here ts-loader 3.5.0 because vue-cli still uses webpack 3 inside. And ts-loader 4 works only with webpack 4.

To use typescript we should add a configuration file for it. Let's create tsconfig.json for this.

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "module": "es2015",
    "moduleResolution": "node",
    "sourceMap": true,
    "experimentalDecorators": true,
    "baseUrl": ".",
    "paths": {
      "@": ["src/*"]
    }
  }
}

Now we need to transpile all files with .vue and .ts extensions with typescript before passing them to Vue. We need to add new rule for webpack in webpack.base.conf.js

build/webpack.base.conf.js

...
module: {
  rules: [
    ...(config.dev.useEslint ? [createLintingRule()] : []),
    {
      test: /\.ts$/,
      exclude: /node_modules/,
      loader: "ts-loader",
      options: {
        appendTsSuffixTo: [/\.vue$/]
      }
    },
    ...
  }
}
...

We used ts-loader here and applied it to ts and vue files.

We also need to add ts extension to the list of our extensions.

build/webpack.base.conf.js

extensions: ['.js', '.vue', '.json', '.ts']

We also need to add esModule property to vue-loader.conf.js to tell vue-loader to use ES modules instead of CommonJS.

build/vue-loader.conf.js

...
module.exports = {
  ...
  esModule: true
}

It's time to rename all our js files to ts files in project. In our case we just need to rename src/main.js and src/router/index.js

We also need to import our vue files with extension now.

src/main.ts

import App from './App.vue'

src/router/index.ts

import HelloWorld from '@/components/HelloWorld.vue'

We did all needed changes to work with TypeScript. If we restart our application we shouldn't get any errors.

Let's try to modify HelloWorld component now to see that TypeScript validates our component

src/components/HelloWorld.vue

<script lang="ts">
  import Vue from 'vue'
  export default Vue.extend({
    sss: 1
  })
</script>

As you can see we used lang attribute on our script to show that we are using ts now. We also used Vue.extend and just plain object. In this case Vue typings will be checked and TypeScript will throw the error that we are passing wrong attributes to it.

Let's refactor this code to make it working with Vue.extend.

src/components/HelloWorld.vue

<script lang="ts">
  import Vue from 'vue'
  export default Vue.extend({
    data () {
      return {
        msg: 'Welcome'
      }
    }
  })
</script>

As you can see, it's basically the same notation as before but with Vue.extend. Now our msg is rendered and we don't get any TypeScript errors.

It's very useful to declare your components with class notation. We should install officially maintained library for this

yarn add vue-class-component

Let's refactor our component to class now.

src/components/HelloWorld.vue

<template>
  <div class="hello">
    {{msg}}
    {{computedMsg}}
    <button @click="onClick">Click!</button>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
@Component({
  props: {
    message: String
  }
})
export default class HelloWorld extends Vue {
  msg: string = 'Hello!'

  get computedMsg (): string {
    return 'computed' + this.msg
  }

  onClick (): void {
    this.msg = 'Clicked!'
  }
}
</script>

As you can see we are exporting class here. This gives us a nice opportunity to set instance properties with types. We also wrote computed property with get method here. When we need to pass props to our component we can do this inside @Component decorator that we should always use when writing a class.

As you can see, TypeScript gives a lot of benefits to our code and it doesn't matter which framework we are using.