Tutorial

How To Use TypeScript with Vue Single File Components

Vue.js

Introduction

TypeScript is a JavaScript superset created by Microsoft which turns the loosely-typed language into a strictly-typed language. It can be summarized as ECMAScript 6 with optional type declarations.

Evan You, the creator of Vue.js, has stated that Vue.js 3.0’s codebase will be rewritten entirely in TypeScript.

In this tutorial, you will use @vue/cli to generate a new Vue.js 2.0 application with TypeScript and build a Single-File Component (SFC).

Prerequisites

To follow along with this article, you will need:

This tutorial was verified with Node v15.1.0, npm v6.14.8, Vue.js v2.6.11, TypeScript v3.9.3, and @vue/cli v4.5.0.

Step 1 — Setting Up the Project

With Vue CLI 3+, it is possible to generate a new project with TypeScript already configured.

When using vue create, you will be prompted with project configurations:

Screenshot of Vue CLI prompts for picking a preset and adding TypeScript

For the purposes of this tutorial, the configuration will require:

Prompt Option
Please pick a preset Manually select features
Check the features needed for your project TypeScript

Note: While bootstrapping a new project, you will also be prompted with: Use class-style component syntax?.

Back in February, Evan You abandoned the class style syntax for the Vue 3.0 core library altogether.

It is a popular choice, but this tutorial will be going over how to use TypeScript with Vue.js without it. If you would like to explore a project with class-style components, consult Writing Class-Based Components with Vue.js and TypeScript.

Selecting “TypeScript” will do several things. It will automatically add @vue/cli-plugin-typescript to your project. It will replace main.js with main.ts. It will also add shims-tsx.d.ts, and shims-vue.d.ts.

Note: If you already have a Vue project created and want to add TypeScript support to it, you can do so with the following:

  • vue add typescript

After generating your Vue project from the command line, you might have noticed the shims-vue.d.ts file. That is the declaration file (.d.ts).

A declaration file is a file that does not contain any executable code but contains descriptions of code that exists outside of the project files.

These are useful when using Node modules that do not contain any TypeScript interfaces, types, or declaration files. In a sense, the only purpose for these files is to tell TypeScript how to handle external code.

shims.d.ts
declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}

This code essential tells the TypeScript compiler (and the IDE) how to handle .vue files.

Once you have a Vue project with TypeScript, you can configure tsconfig.json.

Step 2 — Configuring the TypeScript Compiler

You can configure TypeScript to the needs of your team or project. There are many options that you can enable or disable with the use of a tsconfig.json file. This file should live in the root directory of your project.

Feel free to experiment with these options to find which is most useful for you and your project.

noImplicitAny, noImplicitThis, noImplicitReturns are options that will likely be beneficial to most situations:

  • noImplicitAny: Raise error on expressions and declarations with an implied any type. This will throw an error if an argument, const, let, or var doesn’t have a type. This is more of a mental check on yourself to create custom data types for your code.
  • noImplicitThis: Similar to noImplicitAny but will throw an error with the this keyword. Another mental check to encourage you to type everything you can.
  • noImplicitReturns: Report an error when not all code paths in the function return a value. This helps ensure that all conditions in a given function with a return type, returns a value.

Here is an example tsconfig.json:

tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "strict": true,
    "noImplicitAny": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "baseUrl": ".",
    "types": [
      "webpack-env"
    ],
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

More information about tsconfig.json is available in the official documentation.

Step 3 — Using Basic and Custom Data Types

In TypeScript, 12 basic types are supported:

  • boolean
  • number
  • string
  • array
  • object
  • tuple
  • enum
  • any
  • void
  • undefined
  • null
  • never

The more common types that you will be using are the primitive types: string, number, boolean, null, undefined, and void.

However, there will be times when you will need to create a custom data type. For those situations, you can create something that TypeScript calls an Interface.

In your root directory, create a directory and name it types:

  • mkdir types

Inside this new directory, create a new file named, index.ts. You can declare a new interface with the interface keyword:

types/index.ts
export interface User {

}

Note: It is considered best practice to use CamelCase for naming conventions.

From here, you can start defining the properties and value types that the object will have.

types/index.ts
export interface User {
  firstName: string,
  lastName: string,
  twitterHandle: string,
  location: {
    city: string,
    state: string
  }
}

In this example, you have an interface with an object inside it (location). This can be broken up further by nesting interfaces.

You can also make any property optional by adding a ? to it. This means that this property may or may not have a value.

Here is the previous index.ts rewritten with those changes:

types/index.ts
export interface User {
  firstName: string,
  lastName: string,
  twitterHandle?: string,
  location: Location
}

export interface Location {
  city: string,
  state: string
}

You can now use this custom data type in any Single-File Vue Component (.vue) or TypeScript (.ts) file.

Step 4 — Using Custom Data Types in Single-File Vue Components (SFCs)

Here is an example for App.vue that uses the User interface and displays the firstName and lastName:

src/App.vue
<template>
  <p>{{ fullName }}</p>
</template>

<script>
  export default {
    name: 'Home',
    data() {
      return {
        user: {}
      }
    },
    computed: {
      fullName() {
        return `${this.user.firstName} ${this.user.lastName}`
      }
    },
    mounted() {
      this.user = {
        firstName: `Sammy`,
        lastName: `Shark`,
        twitterHandle: `@digitalocean`,
        location: {
          city: `New York City`,
          state: `NY`
        }
      }
    }
  }
</script>

In order to use TypeScript in this component, you will need to add a lang attribute to the script tag of your component. The value of that attribute should be ts.

When using TypeScript in single-file Vue components, the Vue library must be imported so you can extend from it.

Since you will not be using the class-style syntax, you use the as keyword to declare data as a data type.

For things like const, let, var, or a function return type, you can define its type with the colon (:).

After applying these changes to App.vue, it now resembles:

src/App.vue
<template>
  <p>{{ fullName }}</p>
</template>

<script lang="ts">
  import Vue from 'vue'
  import { User } from '../types'

  export default Vue.extend({
    name: 'Home',
    data() {
      return {
        user: {} as User
      }
    },
    computed: {
      fullName(): string {
        return `${this.user.firstName} ${this.user.lastName}`
      }
    },
    mounted(): void {
      this.user = {
        firstName: `Sammy`,
        lastName: `Shark`,
        twitterHandle: `@digitalocean`,
        location: {
          city: `New York City`,
          state: `NY`
        }
      }
    }
  })
</script>

This code imports the User interface and declares reactive data as type User.

The computed property returns a string, so a string type is defined. The mounted hook returns nothing, so a void type is defined. With these definitions in place, there will be no TypeScript errors when compiling.

Compile the application:

  • npm run serve

When you open the result in a browser, you should observe the full name displayed.

Conclusion

TypeScript is JavaScript. With TypeScript, you can be as strict or as lenient as you want. It helps keep your code base consistent and scalable as your project continues to grow.

TypeScript is also heavily integrated with various popular IDEs and editors (including VS Code, WebStorm, Sublime, Vim, Atom, and more). With these editors, TypeScript works in the background, behind the scenes to provide real-time clues, suggestions, and previews of function arguments and return types.

All-in-all, it is a language that continues to find its way into more tools, libraries, and frameworks that developers use every day. It has a strong Open Source community and the backing of Microsoft.

If you’d like to learn more about TypeScript, check out our TypeScript topic page for exercises and programming projects.

Creative Commons License