Why is this an issue?

This rule raises an issue on a class declaration or class expression that extends the Vue base class imported from vue-class-component, or that is decorated with @Component or @Options imported from that same library. It also raises an issue when a class-based component uses a decorator from vue-property-decorator, such as @Prop, @PropSync, @Model, @ModelSync, @Watch, @Provide, @Inject, @ProvideReactive, @InjectReactive, @Emit, @Ref, or @VModel, on the class or one of its members.

This rule only applies to projects on Vue 2.7 or later, where the Composition API is available. It does not raise an issue on older Vue 2 projects, since migrating away from the Class API would first require upgrading Vue itself.

vue-class-component is a decorator-based library that lets Vue components be written as ES/TypeScript classes. Its own repository states that it is no longer actively maintained and that class-based components are no longer recommended for Vue 3, in favor of the Composition API used through the setup() option or <script setup>.

vue-property-decorator builds on top of vue-class-component to add decorators such as @Prop and @Watch for class-based components. It is equally deprecated: its repository was archived on February 7, 2024, and its documentation states that it is no longer actively maintained and points to the same replacement, vue-facing-decorator.

Building or keeping Vue 3 components on top of either library carries several risks:

To fix this issue, rewrite the component with the Composition API, preferably using <script setup> for its more concise syntax: declare props with the defineProps macro, replace class fields with ref/reactive state, and move methods into plain functions. When using the setup() option instead, declare props the same way as with the Options API; setup(props) then receives them as its first argument, while reactive state and methods are declared inside setup() and returned for the template to use.

To keep the component’s behavior unchanged, keep prop names and types identical, initialize ref/reactive state from the same value as the original class fields, and move lifecycle methods such as mounted or created into their Composition API equivalents, for example onMounted or code placed directly in setup(). The same mapping applies to vue-property-decorator’s decorators: for example, `@Prop maps to a declared prop, @Watch maps to a watch() call, and @Emit maps to a plain function that calls the component’s emit.

If the project still needs class syntax for another reason, consider migrating to vue-facing-decorator, a community-maintained alternative that is not affected by this rule. If migrating immediately is not realistic, for example because a large legacy codebase depends on vue-class-component or vue-property-decorator across many components, treat the issue as a backlog item for the eventual move to the Composition API rather than as a false positive: the underlying dependency risk remains until the migration happens.

Code examples

Noncompliant code example

<script>
import { Options, Vue } from 'vue-class-component';

@Options({
  props: {
    message: String,
  },
})
export default class MyComponent extends Vue { // Noncompliant: relies on the deprecated "vue-class-component" library
  count = 0;
}
</script>

Compliant solution

<script setup>
import { ref } from 'vue';

defineProps({
  message: String,
});

const count = ref(0);
</script>

Noncompliant code example

<script>
import { Vue } from 'vue-class-component';

export default class MyComponent extends Vue { // Noncompliant: relies on the deprecated "vue-class-component" library
  count = 0;

  increment() {
    this.count++;
  }
}
</script>

Compliant solution

<script setup>
import { ref } from 'vue';

const count = ref(0);

function increment() {
  count.value++;
}
</script>

Noncompliant code example

<script lang="ts">
import { Vue, Prop } from 'vue-property-decorator';

export default class MyComponent extends Vue { // Noncompliant: relies on the deprecated "vue-property-decorator" library
  @Prop() readonly msg!: string;
}
</script>

Compliant solution

<script setup lang="ts">
defineProps<{ msg: string }>();
</script>

Resources

Documentation