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.