Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Combine asyncData return type with Vue component data type. #48

Closed
andoshin11 opened this issue Jan 30, 2019 · 12 comments
Closed

Combine asyncData return type with Vue component data type. #48

andoshin11 opened this issue Jan 30, 2019 · 12 comments
Labels
transfered Issue transfered from nuxt main repo

Comments

@andoshin11
Copy link

What problem does this feature solve?

While we can access the returned type information of Vue native apis (i.e. data, computed, props, methods), the return type of asyncData function is totally lost from "this" object.

I'm not sure if it's even possible to combine these type information without changes on Vue core types, but I believe it is 'must-have' goal for us to provide safe, correct, efficient type definitions for users.

I've already spent some hours to solve this problem yet I haven't seen a daylight. Hope I can rely on your help.

This feature request is available on Nuxt community (#c8568)
@kevinmarrec
Copy link
Contributor

@andoshin11 I might be wrong but I kind of understood you want to access this inside asyncData with the right type ?

import { Component, Vue } from 'vue-property-decorator'

@Component({
  asyncData (this: MyPage, ctx) {
    return {
      test: this.bar() + this.foo // works & returns `foobar`
    }
  }
})
export default class MyPage extends Vue {
  foo: string = 'bar'
  bar () {
    return 'foo'
  }
}

@kevinmarrec
Copy link
Contributor

OR If you asked about having autocompletion/typechecking on the return of asyncData, I don't think it's possible. Or maybe like this :

interface Test {
  propertyOne: string
  propertyTwo: string[]
}

@Component({
  asyncData (this: PageIndex, ctx): Test {
    return { // autocompletion/typecheck on the returned object
      propertyOne: 'foo',
      propertyTwo: ['bar', 'foo', 'bar']
    }
  }
})
export default class MyPage extends Vue implements Test {
  propertyOne = ''
  propertyTwo = [] 
}

@kevinmarrec
Copy link
Contributor

@andoshin11 Any updates around this issue ? I need more info

@kevinmarrec
Copy link
Contributor

@andoshin11 Any updates ?
I don't have any clue about what is exactly the request so I'll close the issue if I don't have any more info (issue being pending since almost 4 months without new comments of you)

@kevinmarrec
Copy link
Contributor

Closing due to ~5 months inactivity.

@pi0 pi0 transferred this issue from nuxt/nuxt Aug 10, 2019
@kevinmarrec kevinmarrec added the transfered Issue transfered from nuxt main repo label Aug 10, 2019
@nuria-fl
Copy link

Hi, I see this is closed but I'm running into the same issue, I think. I'm using the options API, but the problem remains the same. If I have this code:

interface Test {
  foo: string
  bar: number
}

export default Vue.extend({
  asyncData(): Test {
    // this is syncronous to keep the example minimal but would be the same
    return {
      foo: 'hi',
      bar: 1
    }
  },
  methods: {
    test() {
      console.log(this.foo) // error here Property 'foo' does not exist on type…
    }
  }
})

I see you say this is not possible, but it's a pretty core feature of nuxt and I wonder how people overcome this in their projects. The only way I've found to overcome the issue is adapting that last code example to the options API, initialising the variables with the data function, which it makes it clearer that it's very cumbersome and not ideal:

interface Test {
  foo: string
  bar: number
}

export default Vue.extend({
  data(): Test {
    return {
      foo: '',
      bar: 1
    }
  },
  asyncData(): Test {
    return {
      foo: 'hi',
      bar: 1
    }
  },
  methods: {
    test() {
      console.log(this.foo)
    }
  }
})

Let me know if you want me to open a new issue.

@kevinmarrec
Copy link
Contributor

@nuria-fl The infered types from data is done by Vue official types, I don't have any clue yet if we can override it to make it infer both types of data & asyncData.

@HermitSun
Copy link

Hi, I see this is closed but I'm running into the same issue, I think. I'm using the options API, but the problem remains the same. If I have this code:

interface Test {
  foo: string
  bar: number
}

export default Vue.extend({
  asyncData(): Test {
    // this is syncronous to keep the example minimal but would be the same
    return {
      foo: 'hi',
      bar: 1
    }
  },
  methods: {
    test() {
      console.log(this.foo) // error here Property 'foo' does not exist on type…
    }
  }
})

I see you say this is not possible, but it's a pretty core feature of nuxt and I wonder how people overcome this in their projects. The only way I've found to overcome the issue is adapting that last code example to the options API, initialising the variables with the data function, which it makes it clearer that it's very cumbersome and not ideal:

interface Test {
  foo: string
  bar: number
}

export default Vue.extend({
  data(): Test {
    return {
      foo: '',
      bar: 1
    }
  },
  asyncData(): Test {
    return {
      foo: 'hi',
      bar: 1
    }
  },
  methods: {
    test() {
      console.log(this.foo)
    }
  }
})

Let me know if you want me to open a new issue.

Maybe we can use type assertion to let TypeScript know our Vue instance's data type? Just like this:

interface Test {
  foo: string;
  bar: number;
}

export default Vue.extend({
  asyncData() {
    return {
      foo: 'hi'
    };
  },
  data() {
    return {
      bar: 1
    } as Test;
  },
  methods: {
    test() {
      console.log(this.foo);
    }
  }
});

This works for me. And I hope we can solve it in the coming Vue 3.0.

@dora-gt
Copy link

dora-gt commented May 29, 2020

I hope so.

@odanado
Copy link

odanado commented Aug 12, 2020

In the options API, I've probably solved this problem.
The type definition is as follows.

import Vue, { ComponentOptions } from "vue";
import { CombinedVueInstance } from "vue/types/vue";
import { RecordPropsDefinition } from "vue/types/options";

// TODO: update
type Context = any;

type DataDef<Data, Props, V> = Data | ((this: Readonly<Props> & V) => Data);

type DefaultAsyncData<V> =
  | object
  | ((this: V, context: Context) => Promise<object | void> | object | void);

type ThisTypedComponentOptionsWithArrayPropsAndAsyncData<
  V extends Vue,
  Data,
  Methods,
  Computed,
  PropNames extends string,
  AsyncData
> = object &
  ComponentOptions<
    V,
    DataDef<Data, Record<PropNames, any>, V>,
    Methods,
    Computed,
    PropNames[],
    Record<PropNames, any>,
    DataDef<AsyncData, Record<PropNames, any>, V>
  > &
  ThisType<
    CombinedVueInstance<
      V,
      Data,
      Methods,
      Computed,
      Readonly<Record<PropNames, any>>
    > &
      AsyncData
  >;
export type ThisTypedComponentOptionsWithRecordPropsAndAsyncData<
  V extends Vue,
  Data,
  Methods,
  Computed,
  Props,
  AsyncData
> = object &
  ComponentOptions<
    V,
    DataDef<Data, Props, V>,
    Methods,
    Computed,
    RecordPropsDefinition<Props>,
    Props,
    DataDef<AsyncData, Props, V>
  > &
  ThisType<
    CombinedVueInstance<V, Data, Methods, Computed, Readonly<Props>> & AsyncData
  >;

declare module "vue/types/options" {
  interface ComponentOptions<
    V extends Vue,
    Data = DefaultData<V>,
    Methods = DefaultMethods<V>,
    Computed = DefaultComputed,
    PropsDef = PropsDefinition<DefaultProps>,
    Props = DefaultProps,
    AsyncData = DefaultAsyncData<V>
  > {
    asyncData?: AsyncData;
  }
}

declare module "vue/types/vue" {
  interface VueConstructor<V extends Vue> {
    extend<Data, Methods, Computed, PropNames extends string, AsyncData>(
      options?: ThisTypedComponentOptionsWithArrayPropsAndAsyncData<
        V,
        Data,
        Methods,
        Computed,
        PropNames,
        AsyncData
      >
    ): ExtendedVue<V, Data, Methods, Computed, Record<PropNames, any>>;
    extend<Data, Methods, Computed, Props, AsyncData>(
      options?: ThisTypedComponentOptionsWithRecordPropsAndAsyncData<
        V,
        Data,
        Methods,
        Computed,
        Props,
        AsyncData
      >
    ): ExtendedVue<V, Data, Methods, Computed, Props>;
  }
}

export default Vue.extend({
  props: {
    c: {
      type: String,
    },
  },
  asyncData() {
    return { a: 10 };
  },
  data: () => ({ b: 10 }),
  methods: {
    click(): void {
      console.log(this.a, this.b, this.c);
    },
  },
});

As you can see in the image, the type of the property a is completed.
 2020-08-13 1 04 37

@MartinMuzatko
Copy link

@odanado is it possible to place this into one file to re-use for every component? Not sure how modules and namespaces work in TS

@odanado
Copy link

odanado commented Jun 27, 2021

@MartinMuzatko
Yes, it is possible.
However, it is better to wait for the release of nuxt/nuxt#9239 than to do any work on your own.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
transfered Issue transfered from nuxt main repo
Projects
None yet
Development

No branches or pull requests

7 participants