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.
<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>
<script setup>
import { ref } from 'vue';
defineProps({
message: String,
});
const count = ref(0);
</script>
<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>
<script setup>
import { ref } from 'vue';
const count = ref(0);
function increment() {
count.value++;
}
</script>
<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>
<script setup lang="ts">
defineProps<{ msg: string }>();
</script>
<script setup>