Vue + TS without class component ? No way!
Tags: vue, typescript
Published: 2022-09-15 12:47:39

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
```

```vue
<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>
```

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](https://class-component.vuejs.org/guide/installation.html#vue-cli-setup). I was so happy.

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

```vue
<script>
// with vue-class-component

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

Everything was fine until this thunderclap... 

Today, [`vue-cli` is deprecated](https://cli.vuejs.org/) 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6l57kx3amburocbkmq12.png)

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](https://vuejs.org/guide/extras/composition-api-faq.html#what-is-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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vpgxp9gaxcyfgu8fv56a.png)



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

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

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

function myMethod() {
    // code here
}
</script>
```

> 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:

```typescript
// 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
});
```

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/

{% embed https://github.com/facing-dev/vue-facing-decorator %}

And it's very easy to use.

Create a project with TypeScript support:

```bash
npm init vue@3
# ==> and select "TypeScript" support in the wizzard
```

Then, inside the project, install the module:

```bash
npm i --save vue-facing-decorator
```

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

```json
{
    "compilerOptions": {
        "experimentalDecorators": true
    }
}
```

> And now, you can  import `Component` decorator, and `Vue` base class from `vue-facing-decorator`. Easy :smile:

## How to use?

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

```vue
// 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>
```

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

```vue
// 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>
```

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