Using TypeScript with Vue Single File Components

Dave Berning

You might be asking yourself, “What is TypeScript”? Well, it’s one of the fastest growing programming languages that’s what! It’s a JavaScript superset created by Microsoft and turns the loosely typed language into strictly typed one; basically, it’s ES6 with optional type declarations. If you’re unsure where to start with TypeScript, I encourage you to read more about it.

With that said, it’s never been a better time to start using TypeScript in your Vue.js projects. Evan You stated that, Vue.js 3.0’s code base will be rewritten entirely in TypeScript. This won’t affect ES6 developers, but it will by far improve the TypeScript experience.

Generate a CLI Project

With Vue CLI 3, you can generate a new project with TypeScript already configured; just select “TypeScript” in the CLI prompt. While bootstrapping a new project, you’ll notice to that you can opt to use the “class-style component syntax”. If you are new to TypeScript, I recommend to not use the class-style syntax. Back in February, Evan You abandoned the class style syntax for the Vue 3.0 core library altogether. It’s a popular choice, but for those reasons, we will be going over how to use TypeScript with Vue.js without it.

# Generate Vue project in current directory
$ vue create .

Selecting TypeScript at the CLI

Using class-style components or not

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

Configuring the TypeScript Compiler

Since TypeScript requires a build step, you can configure TypeScript to the needs of your team or project. There are a slew of 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. I would recommend enabling noImplicitAny, noImplicitThis, noImplicitReturns at a minimum.

  • 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 function return a value. This helps ensure that all conditions in a given function with a return type, returns a value.

tsconfig.json

{
  "compilerOptions": {
    "module": "ES6",
    "noImplicitAny": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "sourceMap": true
  },
  "paths": {
    "@/*": [
      "src/*"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.vue"
  ],
  "files": [
    "src/vue-shims.d.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}

Basic and Custom Data Types

In TypeScript there 12 different types out of the box:

  • 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 that time, you can create something that TypeScript calls an Interface.

In your root directory, create a directory and name it types. Inside this new directory, create a new file named, index.ts. You can declare a new interface with the interface keyword; it is considered best practice to make these CamelCase.

types/index.ts

export interface User {

}

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, we have an interface with an object inside it (location). We can break this up further by nesting interfaces. We can also make any property optional by adding a ? to it. This means that this property may or may not have a value.

types/index.ts

export interface User {
  firstName: string,
  lastName: string,
  twitterHandle?: string,
  location: Location
}

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

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

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

Now that we have your interface created, we can import this into our SFC like any other ESM, JavaScript file, etc. In order to use TypeScript in our 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 we are not using the class-style syntax, you use the as keyword to declare data as a data type.

App.vue

<template>
  <p></p>
</template>

<script lang="ts">
  import Vue from 'vue'
  import { User } from '@/types' // Our interface
  
  export default Vue.extend({
    name: 'Home' as string,
    data() {
      return {
        user: {} as User // Declaring reactive data as type User
      }
    },
    mounted() {
      this.user = {
        firstName: `Dave`,
        lastName: `Berning`,
        twitterHandle: `@daveberning`,
        location: {
          city: `Cincinnati`,
          state: `OH`
        }
      }
    }
  })
</script>

For things like const, let, var, or a function return type, you can define it’s type with the colon :.

App.vue

<script lang="ts">
  import Vue from 'vue'
  import { User } from '@/types'
  
  export default Vue.extend({
    name: 'Home' as string,
    data() {
      return {
        user: {} as User
      }
    },
    computed: {
      fullName(): string { // Computed Property returns a string
        return `${this.user.firstName} ${this.user.lastName}`
      }
    },
    mounted(): void { // The mounted hook returns nothing
      ...
    }
  })
</script>

Declaration Files

After generating your Vue project from the command line, you might have noticed the shims-vue.d.ts file. That is 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 are to tell TypeScript how to handle external code.

shims.d.ts

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

So in this case, we are essentially telling the TypeScript compiler (and the IDE) how to handle .vue files; a framework-specific file with HTML, CSS, and TS.

Conclusion

TypeScript is to JavaScript as SASS is to CSS; TypeScript is JavaScript. With TypeScript, you can be as strict or as lenient as you want. It’s a great Open Source language that 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’s a language that continues to find it’s way into more tools, libraries, and frameworks that developers use everyday. It has a strong Open Source community and the backing of Microsoft.

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#