Get Started Writing Class-based Vue.js Apps in TypeScript

    Nilson Jacques
    Share

    In September of last year, Evan You (creator of Vue.js) announced plans for the next major version of the library. Vue 3.0 will feature an improved experience for TypeScript users, including native support for class-based components, and better support for type inference when writing code.

    The great news is, you don’t have to wait until version 3.0 is released (predicted for Q3 of 2019) to start writing your Vue apps in TypeScript. Vue’s command-line tool, Vue CLI, comes with options for starting projects with the TypeScript build tooling pre-configured and includes the officially supported vue-class-component module, allowing you to write your Vue components as TypeScript classes.

    This article assumes some familiarity with both Vue and the basics of TypeScript. Let’s take a look and see how you can start taking advantage of static typing and class-based components in your code today.

    Starting a Vue + TypeScript Project

    One hurdle to getting started with TypeScript can be configuring the necessary build tooling. Thankfully, Vue has us covered there with the Vue CLI. We can use it to create a project for us with the TypeScript compiler set up and ready to go.

    Let’s briefly walk through creating a new Vue project with TypeScript support.

    From the terminal/command line (and assuming you have Node.js installed), run the following command to install Vue CLI globally:

    npm install -g @vue/cli
    

    Next, let’s create a new project, specifying the name of the project:

    vue create vue-typescript-demo
    

    This will also be the name of the sub-folder the project is installed to. Once you hit Enter, you’ll be prompted to choose either the default preset, or to manually select the options you wish to have installed.

    Choose the manual option, and you’ll be presented with a further set of options. The essential option is, obviously, TypeScript, but you might also want to select Vuex as we’ll be checking out some Vuex-specific decorators later on.

    Having selected your project options, the next screen will ask you if you want to use the class-style component syntax. Say yes to this. You’ll then be asked if you want to ‘Use Babel alongside TypeScript for auto-detected polyfills’. This is a good idea for projects where you’ll be supporting older browsers. Answer the remaining questions as you see fit, and the installation process should start.

    A Note on Editor/IDE Support

    Many code editors and IDEs now have support for TypeScript. Among paid solutions, JetBrains software (e.g. WebStorm, PhpStorm) has excellent support for both Vue and TypeScript. If you’re looking for a free alternative, my recommendation is Microsoft’s Visual Studio Code: combined with the Vetur extension it provides great auto-completion and type checking.

    Class-based Components

    Let’s start by looking at how to write Vue components using classes. While this feature is not limited to TypeScript, using class-based components helps TS provide better type checking and, in my opinion, makes for cleaner, more maintainable components.

    Let’s take a look at the syntax. If you followed along with the previous section and used Vue CLI to create a new project, go into the project directory, into the src sub-folder, and open App.vue. What we’re interested in here is the <script> section, as it’s the only part that differs from a standard Vue single-file-component (SFC).

    <script lang="ts">
    import { Component, Vue } from 'vue-property-decorator';
    import HelloWorld from './components/HelloWorld.vue';
    
    @Component({
      components: {
        HelloWorld,
      },
    })
    export default class App extends Vue {}
    </script>
    

    Notice that the <script> tag itself has a lang attribute of ts set. This is important for the build tools and your editor to correctly interpret the code as TypeScript.

    In order to declare a class-based component, you need to create a class that extends vue (here it’s imported from the vue-property-decorator package rather than the vue module directly).

    The class declaration needs to be preceded by the @Component decorator:

    @Component
    class MyComponent extends Vue {}
    

    As you might have noticed in the code from the App.vue component, the decorator can also accept an object, which can be used to specify the components, props, and filters options for the component:

    @Component({
      components: { MyChildComponent },
      props: {
        id: {
          type: String,
          required: true
        }
      },
      filters: {
        currencyFormatter
      }
    })
    class MyComponent extends Vue {}
    

    Data properties

    When declaring object-based components, you’ll be familiar with having to declare your component’s data properties as a function that returns a data object:

    {
      data: () => ({
        todos: [],
      })
    }
    

    … whereas with class-based components, we can declare data properties as normal class properties:

    @Component
    class TodoList extends Vue {
      todos: [];
    }
    

    Computed properties

    Another advantage of using classes as components is the cleaner syntax for declaring computed properties, using getter methods:

    @Component
    class TodoList extends Vue {
      // ...
    
      get uncompletedTodos() {
        return this.todos.filter(todo => todo.done === false);
      }
    }
    

    Likewise, you can create writable computed properties by using a setter method:

      set fullName(value: string) {
        let names = newValue.split(' ');
        this.firstName = names[0];
        this.lastName = names[names.length - 1];
      }
    

    Methods

    Component methods can be declared in a similarly clean way, as class methods:

    @Component
    class TodoList extends Vue {
      // ...
    
      addTodo(text) {
        this.todos.push({ text, done: false });
      }
    }
    

    In my opinion, the simple syntax for declaring methods, data properties, and computed properties makes writing and reading class-based components nicer than the original object-based ones.

    Decorators

    We can take things a step further, using the additional decorators provided by the vue-property-decorator package. It provides six additional decorators for authoring class-based components:

    Let’s take a look at three of them that you’ll probably find the most useful.

    @Prop

    Rather than passing a props configuration object to the @Component decorator, you can use the @Props decorator to declare your props as class properties.

    @Component
    class TodoItem extends Vue {
      @Prop
      todo;
    }
    

    As with other decorators, @Prop can accept various arguments, including a type, an array of types, or an options object:

    @Prop(String)
    name;
    
    @Prop([String, Null])
    title;
    
    @Prop({ default: true })
    showDetails;
    

    When using with TypeScript, you should suffix your prop names with the non-null operator (!) to tell the compiler that the prop will have a non-null value (as TS is not aware these values will be passed into the component when it’s initialized):

    @Prop(String) name!: string;
    

    Note that, as shown above, it’s perfectly OK to put the decorator and the property declaration on one line if you want.

    @Emit

    Another handy decorator is @Emit, allowing you to emit an event from any class method. The event emitted will use the name of the method (with camelCase names being converted to kebab-case), unless an alternative event name is passed to the decorator.

    If the method returns a value, this will be emitted as the event’s payload, along with any arguments passed to the method.

    @Emit()
    addTodo() {
      return this.newTodo;
    }
    

    The above code will emit an ‘add-todo’ event with the value of this.newTodo as the payload.

    @Watch

    Creating watchers is nice and simple with this decorator. It takes two arguments: the name of the property being observed, and an optional options object.

    @Watch('myProp')
    onMyPropChanged(val: string, oldVal: string) {
      // ...
    }
    
    @Watch('myObject', { immediate: true, deep: true })
    onMyObjectChanged(val: MyObject, oldVal: MyObject) { }
    

    Summing Up

    I hope this article has shown you that starting to write your Vue apps in TypeScript doesn’t have to be a headache. By using the CLI to start new projects, you can quickly set up the necessary build tooling. The included support for class-based components and the additional decorators will have you writing clean, idiomatic TypeScript in no time!

    Want to learn Vue.js from the ground up? Get an entire collection of Vue books covering fundamentals, projects, tips and tools & more with SitePoint Premium. Join now for just $9/month or try our 7 day free trial.