How can I import a Vue class component with proper types?
Asked Answered
S

1

14

If I have a single-file Vue class component, for instance:

// MyComponent.vue
<template>
  <div></div>
</template>

import Vue from 'vue'
import Component from 'vue-class-component'

@Component({
})
export default class MyComponent extends Vue {
  public message: string = 'Hello!'
}

And I import it somewhere else, and get an instance of it.

import MyComponent from "./MyComponent.vue";
...
const foo = ... as MyComponent;
foo.message = "goodbye";

With a standard Vue CLI 3 setup this gives an error, because it contains shims-vue.d.ts with the following content:

declare module "*.vue" {
  import Vue from "vue";
  export default Vue;
}

As I understand it, this means that whenever you write import Foo from "./Foo.vue", then Foo will just be an alias for Vue, and you won't be able to access its members.

This does appear to be the case if you import from .ts files. If you import from .vue files, it magically works!

Unfortunately all of my tests are .spec.ts files so I can't import the types of any components. This makes testing difficult. Is there any way to fix this?

Sippet answered 17/1, 2020 at 13:56 Comment(4)
I really do not think this is the way to use components. You shouldn't be changing the members of your child component from the parent that way. A better way to do this is to use emitters and bindingsMidterm
Yeah I've heard this for React too, but then how do you handle one-off events? Like "if the user clicks this button it should collapse all tree nodes"? You end up having to basically move all the state and logic to the top-most component that needs to access any of it, which is a pretty rubbish solution. In any case, emitters and bindings are really poorly typed in Vue too. I think I will switch to React which supports proper typing.Sippet
The problem isn't Vue itself but that deprecated class components were used. They never have been good enough for TS despite they are classes. Composition API has to be used instead, it was made TS-friendly in a way similar to React.Skidproof
I looked into the composition API but it definitely still isn't as TS-friendly as React/TSX.Sippet
I
4

The way I do it in my Vue 2 and Vue 3 projects is as follows:

  1. Create a Foo.vue file and add script tag to the Foo.vue.ts file. Here is snippet.

    <template>
       <div>... Your template code here ...</div>
    </template>
    
    <script lang="ts" src="./Foo.vue.ts" />
    
  2. Write component code in Foo.vue.ts file.

import Vue from 'vue'
import Component from 'vue-class-component'

@Component({
})
export default class MyComponent extends Vue {
  public message: string = 'Hello!'
}
  1. Now you can use as keyword.
import MyComponent from "./MyComponent.vue";
...
const foo = obj as MyComponent;
foo.message = "goodbye";
Idiolect answered 29/6, 2021 at 4:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.