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.
@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'
}
}
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 = []
}
@andoshin11 Any updates around this issue ? I need more info
@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)
Closing due to ~5 months inactivity.
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.
@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.
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
datafunction, 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.
I hope so.
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.

@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
@MartinMuzatko
Yes, it is possible.
However, it is better to wait for the release of https://github.com/nuxt/nuxt.js/pull/9239 than to do any work on your own.
Most helpful comment
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:
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
datafunction, which it makes it clearer that it's very cumbersome and not ideal:Let me know if you want me to open a new issue.