DEV Community

Cover image for Vue + TS without class component ? No way!
Patrice Ferlet
Patrice Ferlet

Posted on

Vue + TS without class component ? No way!

It is with a huge disappointment that I almost stopped using Vue, because TypeScript support is oriented towards the Composition API, abandoning Class Components. Fortunately, all is not lost!

TL;DR

# create a Vite based project (official way)
npm init vue@3

# install vue-facing-decorator
# cd [to the project], then
npm -i --save vue-facing-decorator
Enter fullscreen mode Exit fullscreen mode
<template>
    <h2>{{ title }}</h2>
    <span>{{ message }}</span>
    <button @click="onStuff">Click me</button>
</template>

<script>
// use the modul to import Component and Vue
import { Component, Prop, Vue } from "vue-facing-decorator";

// and you can now use class component in Vue3 / Vite projects
@Component
export default class MyComponent extends Vue {
    // Property from the component tag
    @Prop message!: String

    // public methods and properties can be 
    // acccessed from the template too
    title = "Test"
    onStuff() {
        // example of handler...
    }
}
</script>
Enter fullscreen mode Exit fullscreen mode

No need of "hooks", it's pure TS.

I love TS, and I love Vue

I develop, among other things, Web applications with mainly 3 frameworks. One of which leaves me speechless:

  • Vue
  • Angular
  • React (I don't like...)

You notice that I did not suffix the names with "JS", because I use mostly TypeScript.

I use TypeScript for a reason, I like a component to be defined as a class, to have properties and methods, and to use the principles of encapsulation and variable scope. So, in essence, I like OOP, and I'm absolutely a fan of having type checking within TypeScript. It makes the code much more secure.

Previously, using vue-cli to create my projects, I was able to activate TypeScript and "class component" syntax. I was so happy.

So, Vue with TypeScript was closed to what I can do in Angular:

<script>
// with vue-class-component

@Options
export default class MyComponent extends Vue {
    myData = "foo"
    myMehtod() {
         // code here...
    }
}
</script>
Enter fullscreen mode Exit fullscreen mode

Everything was fine until this thunderclap...

Today, vue-cli is deprecated to create a new project. The new way is to use Vite and it does'nt provides vue-class-component module and there is no option to force the "old way".

Vue-Cli is now in maintenace mode

And adding the module fails if the project is Vite based.

Vue(TS) is abandoning vue-class-component, preferring to ask developers to use the Composition API.
See https://github.com/vuejs/vue-class-component/issues/569

And, if you're not sure that Vue doesn't like class (exactly like React, and it's a shame in my opinion), read this:
https://vuejs.org/guide/extras/composition-api-faq.html#will-options-api-be-deprecated

It's now clear, Vue doesn't appreciate class components

So... Here is the "new appreciated way to do" so far:

<script setup lang="ts">
// now...
import { reactive, ref } from "vue";

const myData = ref("foo") // or raeactive()

function myMethod() {
    // code here
}
</script>
Enter fullscreen mode Exit fullscreen mode

What's the problem dude?

You probably think it's cool. It's easier to write the component logic for those who used to work in JS.

And, be honnest, it's now closed to what we can see in React.

And I don't like React. I've got many reasons that I will expose in a future article. But the substance is this:

We lose the notion that a component is an "object". We also need to call functions to declare that a variable is reactive, and we need a "JS" syntax to create computed functions for example.

That's not pretty.

To illustrate what I mean:

// This is somehting I prefer

@Component
class Example extends Vue {
    private msg = "Hello";
    title = "The title";

    aMethod(){
        // this method can be called in my template
    }

    // make a computed "message" variable
    set message(val: String) {
        this.msg = val.trim();
    }
    get message():String {
        return this.msg;
    }
}

// This is what to do in a "setup" tag
import { computed, ref } from "vue";

const msg = "Hello";
const title = ref("The Title");

// make a computed "message" variable
const message = computed({ 
    set(val: String) => {msg = val.trim()},
    get():String => this.msg
});
Enter fullscreen mode Exit fullscreen mode

Of course, there is less of code lines. But the readability is bad (my opinion, of course).

The good paradigm I have in mind is that a component is an object that I can instantiate. So, it must be a class.

If I want a poor support of TypeScript, using hooks or functions to declare typed objects, reactive (or ref for primitives) or computed variables, so I can use React... And, as said earlier, I don't like React.

The solution!

By chance, I'm not the only one who loves to work with TypeScript using the strongness of the langage.

There is a fantastic module named vue-facing-decorator here: https://facing-dev.github.io/vue-facing-decorator/

GitHub logo facing-dev / vue-facing-decorator

Vue typescript class component decorators

Read me

GitHub npm npm peer dependency version (scoped)

Designed for vue 3, do the same work like vue-class-component and vue-property-decorator.

  • Community desired vue class component with typescript decorators.
  • Safety. Transform ES class to vue option api according to specifications.
  • Performance. Once transform on project loading, ready for everywhere.
  • Support ES class inheritance, vue extends and vue mixins.

Welcome to suggest and contribute.


支持vue 3, 提供类似vue-class-componentvue-property-decorator的功能。

  • 通过TypeScript装饰器实现社区期望的vue类组件开发。
  • 安全, 根据规范将ES类转换到vue option api。
  • 高性能,仅在项目加载时转换一次。
  • 支持ES类继承,vue extends 和 vue mixins

欢迎提出建议,贡献代码。

Document

To document

中文文档

Discussion

To discord https://discord.gg/4exxtFgkcz

QQ群号 766350254

Recommend packages

recursive-free

Github

Make your recursive-like functions leave stack overflow.

  • Simulate function stack to void stack overflow in recursive-like functions.
  • High performance implemented by JavaScript Map.

@facing/event-bus

Github

Event bus JavaScript implementation

And it's very easy to use.

Create a project with TypeScript support:

npm init vue@3
# ==> and select "TypeScript" support in the wizzard
Enter fullscreen mode Exit fullscreen mode

Then, inside the project, install the module:

npm i --save vue-facing-decorator
Enter fullscreen mode Exit fullscreen mode

In the tsconfig.json file, there is a section named compilerOptions, only add this:

{
    "compilerOptions": {
        "experimentalDecorators": true
    }
}
Enter fullscreen mode Exit fullscreen mode

And now, you can import Component decorator, and Vue base class from vue-facing-decorator. Easy 😄

How to use?

It's pretty simple. Create a component like this:

// example, TodoItem.vue
<template>
   <li> <input type="checkbox" v-model="done" /> {{ title }}</li>
</template>

<script lang="ts">
import { Component, Vue } from "vue-facing-decorator";

@Component
export default class TodoItem extends Vue {
    done = false
    title = ""
}
</script>
Enter fullscreen mode Exit fullscreen mode

Of course, you can use properties, for example to send a "Todo" object in the <TodoItem :todo="todo" /> component:

// example, TodoItem.vue
<template>
    <li> 
        <input 
            type="checkbox"
            v-model="todo.done" />
        {{ todo.title }}
    </li>
</template>

<script lang="ts">
import Todo from "@/classes/todo";
import { Component, Vue } from "vue-facing-decorator";

@Component
export default class TodoItem extends Vue {
    @Prop todo!: Todo;
}
</script>
Enter fullscreen mode Exit fullscreen mode

Computed are simple getters and setters (with get and set keywords), methods are simple methods, ...

You can see the documentation to see how to use the others decorators (like @Watch for example) and the options that you can pass to them.

Conclusion

I have absolutely nothing against the fact that you may like the Vue Composition API. It's just a way of coding that doesn't suit me. I have a clear preference for Angular precisely because this Framework uses TypeScript in the most beautiful way.

Vue, with vue-class-component brought me the best of both worlds. Beautiful TypeScript, with the ease inherent to Vue. The abandonment of this module almost drove me away.

But the vue-facing-decorator module makes up for it in a big way. And I thank the authors of this module.

Note: There is also another project that provides class component at setup stage. I'm not satisfied, but maybe you can appreciate: https://github.com/fmfe/vue-class-setup

Top comments (3)

Collapse
 
rdhelms profile image
Robby Helms

Thanks for this! We ran into a lot of these same considerations about Vue 3:
Vue 2 to Vue 3 with class components

Collapse
 
fasterinnerlooper profile image
Shafiq Jetha

How does it compare to vue-demi?

Collapse
 
metal3d profile image
Patrice Ferlet

Vue-demi is not made for the same goal. It is not (AFAIK) made for TypeScript and it doesn't propose class API.