Developing Vue Components with TypeScript

How I Got Here

My first time working on an SPA project was back in 2012. It was relatively a big-scale project that was based on hottest JavaScript library of the year — Backbone. Back then, the front-end development environment was not what it is today, and we’ve only started to hear murmurs about the concept of SPA. The whole of front-end camp was full of frameworks enslaved by MVC pattern. While people were arguing whether AngularJS conformed to MVC pattern or not, the React subspecies entered the playing field. React, framework influenced by reactive programming and functional paradigm, grew rapidly. While I was bellyaching over the fact that the genius minds that came up with this were younger than I am, a new framework called Vue also threw its name into hat. Vue was affected by many different frameworks, and while the code structure resembled React, it thrived when it came to deal with the states using reactive. Eventually, Vue built its reputation that rivals React by being easy to use. While web developers using React and Redux often run into issues in real-world problems even if they have complete understanding of the immutable foundation of React, the number of web developers using Vue is growing every day.

JavaScript and Vue Component

I apologize for the lengthy introduction. To be fair, I simply cannot believe how much has changed and how far the world of development has come. The codes that I am about to share with you are in files incomprehensible by SFC browsers, and the language is not even technically JavaScript. The codes presented to you in the browser when you’re debugging are not the codes that are used, but only are mere ghostly figments of the restructured source map. Even as I am describing this, I am getting nostalgic, so without further ado, I will continue on with the article.

<template>
<div>
<input type="text" v-model="newTodo" @keyup.enter="onEnter">
<ul>
<li v-for="todo in todos">{{todo}}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
todos: ['TASK1'],
newTodo: ''
};
},
methods: {
onEnter(ev) {
this.addTodo(this.newTodo);
},
addTodo(title) {
this.todos.push(title);
}
}
};
</script>

Vue.extend

There are two ways to implement TypeScript onto the Vue component. One is using Vue.extend to objectify it, or you can simply make Class-based components. For the example, in order to fully differentiate the components’ characteristics, I used both methods to facilitate easier understanding. Let’s first take a look at using Vue.extend method. When the component is defined using the Vue.extend, it looks similar to the version of the component without the TypeScript.

<template>
<div>
<input type="text" v-model="newTodo" @keyup.enter="onEnter">
<ul>
<li v-for="todo in todos">{{todo}}</li>
</ul>
</div>
</template>

<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
data() {
return {
todos: ['TASK1'],
newTodo: ''
};
},
methods: {
onEnter(ev: UIEvent) {
this.addTodo(this.newTodo);
},
addTodo(title: string) {
this.todos.push(title);
}
}
});
</script>
interface Todo {
title: string;
}
export default Vue.extend({
props: {
todos: {
type: Todo[],
required: true,
default: []
}
},
...
export default Vue.extend({
props: {
title: {
type: String,
required: true,
default: []
}
},
methods: {
...mapActions(['addTodo']),
onEnter(ev: UIEvent) {
this.addTodo(this.newTodo);
}
}
methods: {
addTodo(todo: string) {
this.$store.dispatch(‘addTodo’, todo);
},
onEnter(ev: UIEvent) {
this.addTodo(this.newTodo);
}
}

Class based component

Since using Vue.extend and objects had issues with TypeScript, I theorized that when using TypeScript, it would be better to design the components in classes. I believed Vue.extend is a method that puts more weight on the Vue framework as a whole, and class based components put more weight on the language itself. While you could use class based components in ES6, since Vue is built with the foundation that components are created using component constructing option objects, I maintain that object form is still appropriate in ES6. Although I have no idea how things will change in Vue 3, I would like to believe that Vue and I share the common belief. The discussion on class based Vue component does not yet have a concrete answer, but is rather being continuously debated. Perhaps, without TypeScript, such discussion may not have even emerged.

<template>…</template><script lang="ts">
import {Component, Vue, Prop} from 'vue-property-decorator';
import {mapActions} from 'vuex';
@Component({
methods: {
...mapActions(['addTodo'])
}
})
export default class Todolist extends Vue {
public newTodo: string = '';
public addTodo!: (title: string) => void; @Prop({required: true})
public todos!: Todo[];
public onEnter(ev: UIEvent) {
this.addTodo(this.newTodo);
}
}
</script>
@Prop({required: true})
public todos!: Todo[];
@Component({
methods: {
...mapActions(['addTodo'])
}
})
public addTodo!: (title: string) => void;

Conclusion

In the end, I decided to adhere to the Do-not-Repeat-Yourself (DRY) Principle, and decided against using the Map-Helpers. Mapping store elements using Map-Helpers, depending on the component, created long codes to begin with, but now with type repetitions, the code became unnecessarily complicated. If you also consider having to change types in different situations, I was petrified. The components of my current project look like the following.

<template>…</template>
<script lang="ts">
import {Component, Vue, Prop} from 'vue-property-decorator';
@Component
export default class Todolist extends Vue {
public newTodo: string = '';
@Prop({required: true})
public todos!: Todo[]
get schedule(): Schedule {
return this.$store.state.schedule;
}
public onEnter(ev: UIEvent) {
this.$store.dispatch('addTodo', this.newTodo);
}
}
</script>
  • While state and getter can be defined as computed under certain circumstances o be used directly through $store, avoid using $store in the template itself. It only adds complexity to the template.

--

--

JavaScript UI Library Open Source by http://ui.toast.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store