How to set up and code Nuxt.js apps fully in TypeScript
Tags: nuxt, typescript, tutorial
Published: 2020-04-07 16:02:56

---
title: How to set up and code Nuxt.js apps fully in TypeScript
published: true
date: 2020-03-17 17:30:11 UTC
tags: nuxt,typescript,tutorial
canonical_url: https://blog.logrocket.com/how-to-set-up-and-code-nuxt-js-apps-fully-in-typescript/
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/v6usz3z2fnm6z4ip3wx4.png
---
**Written by [Preetish HS](https://blog.logrocket.com/author/preetishhs/)**✏️

Writing JavaScript code in TypeScript can help reduce errors and facilitate collaboration, among other benefits. Although Nuxt provides built-in support for writing code in TypeScript, you still need to rely on a few other libraries to take full advantage of its features.

In this tutorial, we’ll demonstrate how to build Nuxt apps in TypeScript. We’ll create a new Nuxt.js app and install a few packages. Let’s get started!

## Installation

To install Nuxt.js, enter the following line of code.

```jsx
npx nuxt-create-app nuxt-ts-app
```

You’ll be asked to choose name, description, framework, etc. Select the `universal` app and use the defaults for the remaining selections.

After successfully creating the app, navigate to the app directory and install the following packages.

```jsx
cd nuxt-ts-app
npm install --save-dev @nuxt/typescript-build
```

Now we have all the necessary packages loaded. Unlike [Vue](https://dev.to/bnevilleoneill/how-to-write-a-vue-js-app-completely-in-typescript-31ch), which automatically generates configuration files, we need to create them manually.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## Configuration

Add `@nuxt/typescript-build` to your `buildModules` in `nuxt.config.js`.

```jsx
// nuxt.config.js
export default {
  buildModules: ['@nuxt/typescript-build']
}
```

Create the `tsconfig.json` file and add the following.

```jsx
// tsconfig.json
{
  "compilerOptions": {
    "target": "es2018",
    "module": "esnext",
    "moduleResolution": "node",
    "lib": [
      "esnext",
      "esnext.asynciterable",
      "dom"
    ],
    "esModuleInterop": true,
    "allowJs": true,
    "sourceMap": true,
    "strict": true,
    "noEmit": true,
    "baseUrl": ".",
    "paths": {
      "~/*": [
        "./*"
      ],
      "@/*": [
        "./*"
      ]
    },
    "types": [
      "@types/node",
      "@nuxt/types"
    ]
  },
  "exclude": [
    "node_modules"
  ]
}
```

Now create `vue-shim.d.ts` and add the following.

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

You can also install `eslint` for TypeScript. If you already selected `eslist` when creating the app, you can remove that first.

```jsx
npm remove @nuxtjs/eslint-config
npm i -D @nuxtjs/eslint-config-typescript
```

Now update the lint script to:

```jsx
"lint": "eslint --ext .ts,.js,.vue ."
```

We’re good to go! Let’s write some TypeScript code to double-check. You can either use the Options API style (vanilla) or the class-based components style. Let’s see it done both ways.

## Options API (vanilla)

This is straightforward, basic typing that we can do without changing the JavaScript code much.

The syntax will look very similar to JavaScript code.

```jsx
<template>
  <div class="container">
    <p>FirstName: {{ firstName }}</p>
    <p>LastName: {{ lastName }}</p>
    <p>FullName: {{ fullName }}</p>
    <div>Calculate Age:</div>
    <input v-model="year" type="number" />
    {{ ageText }}
  </div>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe',
      year: null,
      ageText: 'Age'
    }
  },
  computed: {
    fullName(): string {
      return this.firstName + this.lastName
    }
  },
  watch: {
    year(newVal: number) {
      this.ageText = this.calculate(newVal)
    }
  },
  methods: {
    calculate(newVal: number): string {
      return 'Age:' + newVal
    }
  }
})
</script>
```

You can do basic typing, such as return types of `computed properties` and `methods`, arguments passed to `watchers`, and `methods`.

## Vuex typing (vanilla)

[Vuex](https://blog.logrocket.com/whats-new-in-vuex-3-1-1-d796b0c791bf/) supports basic typing functionality out of the box.

```jsx
import { GetterTree, ActionTree, MutationTree } from 'vuex'
export const state = () => ({
  count: 0 as number
})
export type RootState = ReturnType<typeof state>
export const getters: GetterTree<RootState, RootState> = {
  count: state => state.count
}
export const mutations: MutationTree<RootState> = {
  CHANGE_COUNT: (state, newVal: number) => (state.count = newVal)
}
export const actions: ActionTree<RootState, RootState> = {
  updateCount({ commit }, newVal) {
    // Some async code
    commit('CHANGE_COUNT', newVal)
  }
}
```

To map these Vuex store items to your components, you would still need to use the traditional `this.$store` or Vuex helpers such as `mapState`, `mapMutations`, etc.

For more advanced typing using classes and the decorators syntax, we used the class-based approach.

## Class-based API

In a class-based API style, we’ll leverage the `nuxt-property-decorator` library, which internally uses `vue-property-decorator`, `vue-class-component`, and `vuex-class` and adds more Nuxt-specific decorators.

To install the library:

```
npm install --save nuxt-property-decorator
```

Let’s see how we can initialize the class in a single-file Vue component. Most of what we do with respect to the class-based API is similar to how we would use class-based TypeScript in Vue since it uses the same libraries under the hood. However, Nuxt has other specific decorators that we’ll also look at.

### Initializing a class

Use the following code to initialize a class.

```jsx
//Typescript code
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
@Component
export default class MyStore extends Vue {
}
</script>
```

The JavaScript-equivalent code would be:

```jsx
<script>
export default {
name: 'MyStore'
}
</script>
```

To use TypeScript in a Vue SFC file, similar to the Option API method, you need to set the `lang` attribute of the `script` tag to `ts`.

## Importing a component

The code to register components inside the other components is written inside the `@Component` decorator.

```jsx
<script lang="ts">
import Tile from '@/components/Tile.vue'
import { Vue, Component } from 'nuxt-property-decorator'
@Component({
  components: {
    Tile
  }
})
export default class MyStore extends Vue {}
</script>
```

The JavaScript-equivalent code would be:

```jsx
<script>
import User from '@/components/Tile.vue'
export default {
  name: 'MyStore',
  components: {
    Tile
  }
}
</script>
```

## Using data, props, computed properties, methods, watchers, and emit

### Data

To use data properties, simply declare them as class variables.

```jsx
export default class MyStore extends Vue {
  title: string = 'Product Categories'
  categoryList: Array<object> = [
    {
      name: 'Phones',
      link: '/phones',
      image: 'iphone-11.png'
    },
    {
      name: 'Laptops',
      link: '/laptops',
      image: 'macbook.png'
    }
  ]
}
```

The JavaScript-equivalent code would look like this:

```jsx
export default {
  title: 'Product Categories'
  categoryList: [
    {
      name: 'Phones',
      link: '/phones',
      image: 'iphone-11.png'
    },
    {
      name: 'Laptops',
      link: '/laptops',
      image: 'macbook.png'
    }
  ]
}
```

### Props

We can use the `@Prop` decorator to use props in our Vue component. In Vue, we can give additional details for props, such as `required`, `default`, and `type`. We first import the `Prop` decorator from `vue-property-decorator` and write it as shown below. We could also use `readonly` to avoid manipulating the props.

```jsx
import { Component, Prop, Vue } from 'nuxt-property-decorator'
@Component
export default class Tile extends Vue {
  @Prop({ required: true }) readonly item!: object
  @Prop() quantity!: number
  @Prop({ default: 'Apple' }) brand!: string
  @Prop(String) readonly type!: string
  @Prop({ required: false, type: String, default: 'Available' })
  readonly stock!: string
}
```

The JavaScript-equivalent code would be as follows.

```jsx
export default {
  props: {
   item: {
      required: true  
  },
   quantity,
   brand: {
      default: 'Apple',
    },
   type: {
      type: String
    },
   stock: {
      required: false,
      type: string,
      default: 'Available'
    }
  }
}
```

### Computed properties

A computed property is used to write simple template logic, such as manipulating, appending, or concatenating data. In TypeScript, a normal computed property is also prefixed with the `get` keyword.

```jsx
export default class Tile extends Vue {
  get buttonText(): string {
    if (this.quantity) {
      return 'Buy Now!'
    } else {
      return 'Coming Soon!'
    }
  }
}
```

Here is the JavaScript-equivalent code:

```jsx
export default {
  buttonText() {
   if (this.quantity) {
      return 'Buy Now!'
    } else {
      return 'Coming Soon!'
    }
  }
}
```

You can write complex computed properties, which have both `getter` and `setter`, in TypeScript as follows.

```jsx
export default class MyStore extends Vue {
 get searchText() {
    return this.searchTextValue
  }
  set searchText(val) {
    this.searchTextValue = val
  }
}
```

The JavaScript-equivalent code would be:

```jsx
searchText: {
  get: function () {
    return this.searchTextValue
  },
  set: function (val) {
    this.searchTextValue = val
  }
}
```

### Methods

Like normal class methods, methods in TypeScript have an optional access modifier.

```jsx
import { Vue, Component } from 'nuxt-property-decorator'
@Component
export default class Laptop extends Vue {
  laptopPrice: number = 1400
  quantity: number = 0
  calculateTotal(): number {
    return this.laptopPrice * this.quantity
  }
}
```

The JavaScript-equivalent code is:

```jsx
export default {
  data() {
    return {
      laptopPrice: 1400
        quantity: 0
    }
  }
  methods: {
    calculateTotal() {
      return this.laptopPrice * this.quantity
    }
  }
}
```

### Watchers

Watcher are written differently than how they are usually written in JavaScript. The most-used syntax for a watcher in JavaScript is:

```jsx
watch: {
  total: function(newval) {
    //do something
  }
}
```

Developers don’t use handler syntax often.

```jsx
watch: {
  total: {
    handler: 'totalChanged'
  }
}
methods: {
  totalChanged(newVal) {
    // do something
  }
}
```

However, the TypeScript syntax is similar to the second method. In TypeScript, you use the `@Watch` decorator and pass the name of the variable you need to watch.

```jsx
@Watch('name')
totalChanged(newVal: string) {
  if(newVal > 20000) {
    this.status = 'limit exceeded for user'
  }
}
```

We can also set the `immediate` and `deep` watchers.

```jsx
@Watch('itemList', { 
  immediate: true, deep: true 
})
itemChanged(newVal: Product, oldVal: Product) {
  // do something
}
```

Here is the JS-equivalent code:

```jsx
watch: {
  itemList: {
      handler: 'itemChanged',
      immediate: true,
      deep: true
    }
}
methods: {
  itemChanged(newVal, oldVal) {
    // do something
  }
}
```

### Emit

To emit a method from a child component to a parent component, use the `@Emit` decorator in TypeScript.

```jsx
@Emit()
addToCount(n: number) {
  this.count += n
}
@Emit('resetData')
resetCount() {
  this.count = 0
}
```

In the first example, the function name `addToCount` is converted to `kebab-case`, similarly to how the Vue emit works.

In the second example, we pass the explicit name `resetData` for the method and that name is used instead. Since `addData` is in `CamelCase`, it is converted to `kebab-case` again.

```jsx
<some-component add-to-count="someMethod" />
<some-component reset-data="someMethod" />


//Javascript Equivalent
 methods: {
    addToCount(n) {
      this.count += n
      this.$emit('add-to-count', n)
    },
    resetCount() {
      this.count = 0
      this.$emit('resetData')
    }
}
```

## Lifecycle hooks

A Vue component has eight lifecycle hooks, including `created`, `mounted`, etc. Nuxt-specific hooks, such as `asyncData` and `fetch`, use the same TypeScript syntax. These are declared as normal class methods. Since lifecycle hooks are automatically called, they neither take an argument nor return any data, so we don’t need access modifiers, typing arguments, or return types.

```jsx
export default class MyStore extends Vue {
  asyncData() {
    //do something
  }
  beforeUpdate() {
    // do something
  }
}
```

The JavaScript-equivalent code is shown below.

```jsx
export default {
  asyncData() {
    //do something
  }
  beforeUpdate() {
    // do something
  }
}
```

## Mixins

To create mixins in TypeScript, first create a mixin file. This contains the data you want to share with other components.

Create a file called `CartMixin.ts` inside the `mixins` directory and add the following mixin, which shares the project name and a method to update the project name.

```jsx
/mixins/CartMixin.ts
import { Component, Vue } from 'nuxt-property-decorator'
@Component
class CartMixin extends Vue {
  public cartProducts: Array<object> = []
  public addToCart(newItem: object): void {
    this.cartProducts = { ...this.cartProducts, ...newItem }
  }
}
export default CartMixin
```

In JavaScript, we’d write this code as follows.

```jsx
export default {
  data() {
    return {
      cartProducts: []
    }
  },
  methods: {
    addToCart(newItem) {
     this.cartProducts = { ...this.cartProducts, ...newItem }
    }
  }
}
```

To use the above mixin in your Vue component, import `Mixins` from `nuxt-property-decorator` and the mixin file itself and write it as follows.

```jsx
//pages/phone/index.vue
<template>
  <div class="phones">
    <div class="item">
      <img src="@/assets/images/iphone-11.png" />
      <div>iphone 11</div>
      <button @click="add">Add to Cart</button>
    </div>
    <div class="cart">
      <div v-for="(item, i) in cartProducts" :key="i" class="item">
        <div>Item: {{ item.name}}</div>
        <div>Quantity: {{ item.quantity }}</div>
      </div>
    </div>
  </div>
</template>
<script lang="ts">
import { Vue, Component, mixins } from 'nuxt-property-decorator'
import CartMixin from '@/mixins/CartMixin'
@Component
export default class Phones extends mixins(CartMixin) {
  public add() {
    this.addToCart({ name: 'phone', quantity: 1 })
  }
}
</script>
```

We are using the `cartProducts` list and the `addToCart` method from our mixin.

The JavaScript-equivalent code would be:

```jsx
<template>
  <div class="phones">
    <div class="item">
      <img src="@/assets/images/iphone-11.png" />
      <div>iphone 11</div>
      <button @click="add">Add to Cart</button>
    </div>
    <div class="cart">
      <div v-for="(item, i) in cartProducts" :key="i" class="item">
        <div>Item: {{ item.name}}</div>
        <div>Quantity: {{ item.quantity }}</div>
      </div>
    </div>
  </div>
</template>
<script>
import CartMixin from '@/mixins/CartMixin'
export default {
  mixins: [ CartMixin],
  methods: {
     public add() {
      this.addToCart({ name: 'phone', quantity: 1 })
    }
  }
}
</script>
```

## Vuex

To create a Vuex store with TypeScript decorators, we’ll use a popular library called `vuex-module-decorators`. You’ll also need a library called `vuex-class` to use these modules in your components. Since `nuxt-property-decorators` internally uses `vuex-class`, we don’t need to install it again.

Install `vuex-module-decorators`.

```jsx
npm install -D vuex-module-decorators
```

Create a new file called `users.ts` in the `store` folder. This will be your users module.

To use the library for Nuxt, you have to explicitly set `stateFactory` to `True`.

```jsx
import { Module, VuexModule, Mutation } from 'vuex-module-decorators'
interface UserData {
  first: string
  last: string
  address1: string
  address2: string
  state: string
  country: string
  phone: number
}
@Module({
  name: 'user',
  stateFactory: true,
  namespaced: true
})
export default class User extends VuexModule {
  public info: UserData = {
    first: 'Preetish',
    last: 'HS',
    address1: '',
    address2: '',
    state: '',
    country: '',
    phone: 9000000009
  }
  get fullName(): string {
    return this.info.first + ' ' + this.info.last
  }
  @Mutation
  public updateUserInfo(data: UserData) {
    this.info = { ...this.info, ...data }
  }
}
```

The `vuex-module-decorators` library provides decorators for `Module`, `Mutation`, and `Action`. The state variables are declared directly, like class variables.

Here we have a `getter` to return the full name and mutation to update the user info. Below is the JavaScript-equivalent code.

```jsx
export default {
  namespaced: true,
  state: {
    info: {
      first: 'Preetish',
      last: 'HS',
      address1: '',
      address2: '',
      state: '',
      country: '',
      phone: 9000000009
    }
  },
  getters: {
    fullName() {
      return this.info.first + ' ' + this.info.last
    }
  }
  mutations: {
    updateUserInfo(data) {
      this.info = { ...this.info, ...data }
    }
  }
}
```

### Using Vuex in components

To use Vuex, you’ll use a library called `vuex-class`. This is already exported from `nuxt-property-decorator`, so we don’t need to install it again. This library provides decorators to bind `State`, `Getter`, `Mutation`, and `Action` in our Vue component.

Since you’re using namespaced Vuex modules, first import `namespace` from `nuxt-property-decorator` and then pass the name of the module to get access to that module.

```jsx
<template>
  <div class="user">
    <div class="title">Welcome {{ fullName }}</div>
    <div>
      First:
      <input type="text" v-model="localData.first" />
    </div>
    <button @click="update">Update Info</button>
  </div>
</template>
<script lang="ts">
import { Vue, Component, namespace } from 'nuxt-property-decorator'
const user = namespace('user')
@Component
export default class User extends Vue {
  public localData: object = {}
  @user.State
  public info!: object
  @user.Getter
  public fullName!: string
  @user.Mutation
  public updateUserInfo!: (data: object) => void
  mounted() {
    this.localData = { ...this.localData, ...this.info }
  }
  public update(): void {
    this.updateUserInfo(this.localData)
  }
}
</script>
```

JavaScript-equivalent code:

```jsx
<script>
import { mapState, mapGetters, mapMutations } from 'vuex'
export default {
  data() {
    return {
      localData: {}
    }
  },
  computed: {
    ...mapState('user', ['info']),
    ...mapGetters('user', ['fullName'])
  },
  mounted() {
    this.localData = { ...this.localData, ...this.info }
  },
  methods: {
    ...mapMutations('user', ['updateUserInfo']),
    update() {
      this.updateUserInfo(this.localData)
    }
  }
}
</script>
```

Visit the [GitHub repo](https://github.com/preetishhs/nuxt-typescript-example) to view the code snippets used in this article.

## Conclusion

Now you have all the basic information you need to create a Nuxt.js application completely in TypeScript using a few official and third-party libraries to fully leverage the typing and custom decorator features. Using TypeScript might seem little overwhelming at first, but when you get used to it, you’ll have far fewer bugs in your code and smooth code collaboration between other developers who work on the same code base.

* * * 
## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *

The post [How to set up and code Nuxt.js apps fully in TypeScript](https://blog.logrocket.com/how-to-set-up-and-code-nuxt-js-apps-fully-in-typescript/) appeared first on [LogRocket Blog](https://blog.logrocket.com).