wagerfield / nuxt-typescript

TypeScript module for Nuxt
MIT License
90 stars 11 forks source link

'asyncData' does not exist in type 'ComponentOptions #6

Open herkulano opened 6 years ago

herkulano commented 6 years ago

Adding the asyncData property and using typescript without classes and decorators, throws this typing error:

Object literal may only specify known properties, and 'asyncData' does not exist in type 'ComponentOptions<Vue, DefaultData<V
ue>, DefaultMethods<Vue>, DefaultComputed, PropsDefinition<Rec...'.

Code:

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  async asyncData({ app }) {
    const hero = await app.$content('home').get('hero')
    return {  hero  }
  }
  }
})
</script>

Do you have any hints on solving this?

wagerfield commented 6 years ago

This is because Vue.extend is expecting an object that satisfies the ComponentOptions interface defined in Vue's types.

Since asyncData, fetch etc. are all mixed in by Nuxt, you will need to augment the ComponentOptions interface defined by Vue (I don't think there are types available from Nuxt yet).

To augment existing TS interfaces within other modules, you need to create a file ending in .d.ts (know as a declaration file) then declare the module you want to augment. I generally create one declaration file per module—so in this case it would be nuxt.d.ts. I also generally put these declaration files in a types directory alongside the other directories, so you would have a dir structure that looked something like this:

pages
- index.vue
types
- nuxt.d.ts

Then the contents of nuxt.d.ts would look something like this:

declare module "vue" {
  import Vue, { ComponentOptions } from "vue"
  import { Route } from "vue-router"
  import { Store } from "vuex"

  // https://nuxtjs.org/api/context
  export interface NuxtContext<V extends Vue> {
    app: V
    isClient: boolean
    isServer: boolean
    isStatic: boolean
    isDev: boolean
    isHMR: boolean
    route: Route
    store: Store
    env: object
    params: object
    query: object
    // ...add remaining interface here
  }

  // https://nuxtjs.org/api/pages-fetch
  export interface ComponentOptions<V extends Vue> {
    asyncData?(context: NuxtContext<V>): Promise<object> | object
    fetch?(context: NuxtContext<V>): Promise<object> | object
    // ...add remaining interface here
  }
}

NOTE 1: It's important that this file is included in the matched files as defined in your tsconfig.json file—otherwise TS won't pick it up.

NOTE 2: I have not tested the above implementation of these "ambient types" so you will probably have to play around a little to appease TS.

If you're struggling to properly type everything, you can start assigning any to each of these interface properties, but you will forfeit strong type checking in the process.

Hope that helps!

wagerfield commented 6 years ago

I will leave this issue open with a view to potentially add these types to this project—but really they belong in Nuxt's repo, so I would perhaps open an issue there and reference this issue.

hartmut-co-uk commented 6 years ago

Hi, have raised a similar issue with nuxt specific fields of ComponentOptions ref: https://cmty.app/nuxt/nuxt-class-component/issues/c8

stepbeekio commented 5 years ago

I've left a working example using the directory structure suggested by @wagerfield above:

https://gist.github.com/stepbeekio/1d5392b13503dca248556f0457e83ffd

Copied below for those in a hurry:

import Vue from 'vue'
import { Store } from 'vuex'

// ComponentOptions is declared in types/options.d.ts
declare module 'vue/types/options' {

    interface NuxtContext<V extends Vue> {
        app: V,
        isClient: boolean,
        isServer: boolean,
        isStatic: boolean,
        isDev: boolean,
        store: Store<any>, // Consider vuex-typex in future
        env: object,
        params: object,
        query: object
    }

    interface ComponentOptions<V extends Vue> {
        asyncData?(context: NuxtContext<V>): Promise<object> | object
        fetch?(context: NuxtContext<V>): Promise<object> | object
    }
}
hartmut-co-uk commented 5 years ago

nice, works for me ✨

wagerfield commented 5 years ago

Thanks @stepbeekio. I haven't abandoned this project—I'm patiently waiting for the release of Nuxt 2 so I can update the implementation to work with Webpack 4 and ts-loader 4.x which is much faster.

hartmut-co-uk commented 5 years ago

Note: I've added types for watchQuery & key on my end.

...
  interface ComponentOptions<V extends Vue> {
    asyncData?(context: NuxtContext<V>): Promise<object> | object
    fetch?(context: NuxtContext<V>): Promise<object> | object
    watchQuery?: string[]
    key?: Function
  }
...
wagerfield commented 5 years ago

@herkulano @hartmut-co-uk @stepbeekio I was thinking about just adding these declarations to this module rather than handing off the responsibility to Nuxt given that it's written in MJS and not TS. Will keep you updated.

ScreamZ commented 5 years ago

@wagerfield I think adding to the module is a good idea. Keep me in touch :)

hartmut-co-uk commented 5 years ago

note added another line to ComponentOptions..:

    middleware?: string | string[]
kevinmarrec commented 5 years ago

@wagerfield I kind of took the lead on this around official Nuxt repo https://github.com/nuxt/nuxt.js/pull/4164.

joffreyBerrier commented 5 years ago

Do like this:

  @Component({
    async asyncData({ params, error }) {
     💀// impossible to use this.$http on @component 💀
      const http = Vue.prototype.$nuxt.$http;
      const objectGet = await http.get('/url')
      const objectPost = await http.post('/url', { params })

      return {
        data1: objectGet.data,
        data2: objectPost.data,
      }
    }
  })
  export default class NameOfYourClass extends Vue {
    name: 'NameOfYourClass'

    // AsyncData
    data1?: type
    data2?: type

    [...]
  }