Typing the Nuxt head() method

Using the head() method in a typed context can be problematic when working with fetch() or asyncData() hooks to retrieve data.

Let's see the following component:

<script lang="ts">
import Vue from 'vue';
import { MetaInfo } from 'vue-meta';

export default Vue.extend({
  async asyncData() {
    // usually you would fetch data here
    const title = 'Blog';
    
    return {
      title
    };
  },
  head(): MetaInfo {
    return {
      title: this.title // <<--- Property 'title' does not exist on type 'CombinedVueInstance<Vue, unknown, unknown, unknown, Readonly<Record<never, any>>>'.
    }
  }
});
</script>

As you see, TypeScript doesn't know about this.title.

My first approach was converting the component to a class-based component using vue-property-decorator like this.

<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
import { MetaInfo } from 'vue-meta';

@Component({
  async asyncData() {
    // usually you would fetch data here
    const title = 'Blog';

    return {
      title
    };
  }
})
export default class PageComponent extends Vue {
  title!: string;
  
  head(): MetaInfo {
    return {
      title: this.title
    }
  }
}
</script>

After this transformation, the type-error is gone, because I told TypeScript about the property via declaring it inside the class.
You would expect this to work..., but it doesn't. Nuxt does not call the head() method anymore, so no metadata is added.

So I had to look further.

Fortunately nuxt-property-decorator has us covered. Just replacing vue-property-decorator with nuxt-property-decorator will do the trick.

So this is my final component:

<script lang="ts">
import { Vue, Component } from 'nuxt-property-decorator';
import { MetaInfo } from 'vue-meta';

@Component({
  async asyncData() {
    // usually you would fetch data here
    const title = 'Blog';

    return {
      title
    };
  }
})
export default class PageComponent extends Vue {
  title!: string;
  
  head(): MetaInfo {
    return {
      title: this.title
    }
  }
}
</script>

Cheers 🥳