Open cawa-93 opened 5 years ago
@cawa-93 Hi.
This issue is caused by the type definition of *.vue
file, and not caused by vue-property-decorator
.
*.vue
files are defined to export Vue
.
I recommend you to ask this question at Vue's official forum or its discord server.
Here is my solution. I usually separate component definition into .vue
and .ts
.
<script>
// CommentForm.vue
import CommentForm from './CommentForm'
export default CommentForm
</script>
// CommentForm.ts
@Component
export default class CommentForm extends Vue {
public focus() {
return 1;
}
}
<script>
// Comments.vue
import Comments from './Comments'
import CommentForm from '@/components/comment-form.vue'
export default Comments.extend({
components: { CommentForm }
})
</script>
// Comments.ts
import CommentForm from '@/components/comment-form';
@Component
export default class Comments extends Vue {
@Ref() readonly commentField!: CommentForm;
public tryFocus() {
this.commentField.focus(); // no error
}
}
I also encountered this problem, do you have any solutions except separating component definition into .vue and .ts?
I'm also experiencing this issue and would like to find a solution that doesn't require separating the component definition into multiple files.
Given the example from @cawa-93 :
export default class Comments extends Vue {
@Ref() readonly commentField!: CommentForm;
public tryFocus() {
this.commentField.focus(); // <- Error: TS2339: Property 'focus' does not exist on type 'Vue'.
}
}
I would expect this.commentField
to be recognized as type CommentForm
, which extends Vue
and has the method focus()
defined, and not as it's ancestor type: Vue
.
EDIT
Excuse my ignorance, I'm just starting to learn typescript...
Alright, I believe this behavior is a result of the declaration file shims-vue.d.ts
. It causes all *.vue
files to be exported as Vue
types. @kaorun343 stated as much, but I (and possibly others) didn't quite understand exactly what he meant. The contents of the file are:
declare module '*.vue' {
import Vue from 'vue';
export default Vue;
}
Anyway, I've found a couple ways around it.
Using // @ts-ignore
. In my editor (Webstorm), this preserves Go To Definition
functionality
@Component
export default class Comments extends Vue {
@Ref() readonly commentField!: CommentForm;
public tryFocus() {
// @ts-ignore
this.commentField.focus(); // no error
}
}
Import it as any
type. This breaks Go To Definition
functionality
@Component
export default class Comments extends Vue {
@Ref() readonly commentField!: any;
public tryFocus() {
this.commentField.focus(); // no error
}
}
Ref
to the interface type. This might be the typescript-purist way to handle this, but it also adds a lot of overhead.
// components/types/index.ts
export interface CommentFormInterface {
focus: () => number;
}
// Child component
@Component
export default class CommentForm extends Vue implements CommentFormInterface {
public focus() {
return 1;
}
}
import CommentForm from '@/components/comment-form.vue';
import { CommentFormInterface } from '@/components/types';
@Component({ components: { CommentForm }, }) export default class Comments extends Vue { // @Ref() readonly commentField!: CommentFormInterface; // Go To Definition navigates to interface definition // or @Ref() readonly commentField!: CommentForm & CommentFormInterface; // Go To Definition navigates to CommentForm component definition.
public tryFocus() { this.commentField.focus(); // no error } }
4. Merge the type. This might be repetitive, but it carries less boilerplate than option 3. It also breaks `Go To Definition`
```ts
@Component
export default class Comments extends Vue {
@Ref() readonly commentField!: CommentForm & { focus: () => number };
public tryFocus() {
this.commentField.focus(); // no error
}
}
As for me, I'll probably just go with option 1 and silence the issue with // @ts-ignore
since this is an issue caused by the declaration in shims-vue.d.ts
. Until something better comes along, and Vue components can export itself intact, everything will be a work-around anyways.
I did handle this issue as following: Create a type with a property $el as a HTMLElement
export type VueComponent = { $el: HTMLElement }
And use solution 4 from @jyork03:
@Component
export default class Comments extends Vue {
@Ref() readonly commentField!: CommentForm & VueComponent
public tryFocus() {
this.commentField.focus(); // no error
}
}
Thus you don't need to write all the HTMLElement methods and properties
Example: