Categories
JavaScript TypeScript Vue

Writing Vue.js Apps with TypeScript

Spread the love

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how to write Vue.js apps with TypeScript.

Support for TypeScript

Vue.js has built-in support for TypeScript. It provides us with a mostly static type system that reduces the chances for run-time errors in our code.

Vue ships with official type declarations for TypeScript. It’s included in Vue, Vue Router and Vuex.

TypeScript can resolve types declarations in NPM packages, so we don’t have to worry about them resolving them ourselves.

Using Vue CLI

We can use the Vue CLI to create a Vue TypeScript project. To do this, we have to run Vue CLI by running:

npx vue create .

in our project directory.

Then we select ‘Manually select features’ and then TypeScript. The rest can stay with the default options.

Then we run npm run serve to run the project hot reload.

If we want, we can also set the following recommended options in tsconfig.json :

{  
  "compilerOptions": {  
    "target": "es5",  
    "strict": true,  
    "module": "es2015",  
    "moduleResolution": "node"  
  }  
}

target is the build target. Setting it to es5 maximizes support for browsers.

strict enables or disables strict type check. Setting it to true will enable it.

“module”: “es2015” leverage tree shaking capabilities of Webpack 2+ or Rollup to reduce bundle size and speed up loading.

Defining and Using Components

We can define components by using the class syntax. For example, with the TypeScript project that we created with Vue CLI, we can do this as follows:

src/components/Message.vue :

<template>  
  <div class="hello">  
    <h1>{{ msg }}</h1>  
  </div>  
</template><script lang="ts">  
import { Component, Prop, Vue } from 'vue-property-decorator';

@Component 
export default class Message extends Vue {  
  @Prop() private msg!: string;  
}  
</script>

In the code above, we have the @Prop decorator to define the message prop. The exclamation mark after msg makes msg required. To the right of the colon, we have the type of the prop, which is a string.

src/App.vue :

<template>  
  <div id="app">  
    <input v-model="message">  
    <Message :msg="message"/>  
    <button @click="showMessage">Show Message</button>  
  </div>  
</template>

<script lang="ts">  
import { Component, Vue } from 'vue-property-decorator';  
import Message from './components/Message.vue';

@Component({  
  components: {  
    Message,  
  },  
})  
export default class App extends Vue {  
  message: string = ''; showMessage(): void{  
    alert(this.message)  
  }  
}  
</script><style>  
#app {  
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif  
}  
</style>

In the code above, we defined the message field, which is bound to the template with v-model .

We also have a showMessage method that’s run when the Show Message button is clicked.

Then we can see an input box where we can type things in. What we type will show in below the input box in the Message component, and when we click Show Message, we get what we typed is shown in the alert box.

Use Vue.extend to Create Components

We can also use Vue.extend to create components.

For example, we can replace src/components/Message.ts with the following”

import Vue, { VNode } from 'vue'
const Component = Vue.extend({  
  props: ['msg'],  
  computed: {  
    greeting(): string {  
      return `Hi. ${this.msg}`;  
    }  
  },  
  render(createElement): VNode {  
    return createElement('div', [  
      createElement('p', this.msg),  
      createElement('p', this.greeting),  
    ])  
  }  
})export default Component;

In the code above, we render a div with the msg prop and greeting computed property.

Note that we have VNode return type for render . This will ensure that we return the right type of data. TypeScript can’t infer types in many cases.

Since we use Vue.extend , we’ll get type inference and autocomplete.

We have to export by writing:

export default Component;

so that it’ll be available to other components.

Then we can import it as usual in src/App.vue :

<template>  
  <div id="app">  
    <input v-model="message">  
    <Message :msg="message"/>  
    <button @click"showMessage">Show Message</button>  
  </div>  
</template><script lang="ts">  
import { Component, Vue } from 'vue-property-decorator';  
import Message from './components/Message';

@Component({  
  components: {  
    Message,  
  },  
})  
export default class App extends Vue {  
  message: string = ''; showMessage(): void{  
    alert(this.message)  
  }  
}  
</script><style>  
#app {  
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif  
}  
</style>

Then we the input where we can type in something and it’ll show up with and without Hi added before it.

We keep the Show Message button acting the way before.

Augmenting Types for Use with Plugins

We can add our own properties to existing types by writing the following:

import Vue from 'vue'declare module 'vue/types/vue' {  
  interface Vue {  
    $myProperty: string  
  }  
}

The code above imports Vue and then add the $myProperty string property as a valid property.

This should make TypeScript compiler be aware of the code and it should compile successfully.

Conclusion

We can create a new project with TypeScript by using the Vue CLI. To do this, we choose Manually Select Features and then TypeScript. We can keep the default option for the rest or we can choose what we want if we know we want it.

To get type inference and autocomplete, we can define components with the Vue.extend method or define it as a class.

We can augment existing Vue types by using the declare module syntax and adding what we want inside it.

TypeScript can’t infer return types of functions in many cases. Therefore, we should annotate the return type so that we get autocomplete and type checking.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *