Takeshi Tokugawa YD
Takeshi Tokugawa YD

Reputation: 923

How to turn the class definition to Vue 3 component definition with "tc39" decorators?

The API purposed in tc39/proposal-decorators is pretty different with previous decorators API. Although the TypeScript 5 does not fully support the new API yet, the deprecating of the previous API is the matter of time, so I'll develop with the newest API.

The answer to question Into what we need to convert the TypeScript class using decorators to get the valid Vue component? is actual for the old API, but now it is required to solve same problem with newest API.

The initial code is

import { defineComponent } from "vue";

/* [ Theory ] Overrides the eponymous type of "typescript/lib/lib.decorators.d.ts".
*  [ Reference ] https://github.com/tc39/proposal-decorators#classes */
type ClassDecorator = (value: Function, context: {
  kind: "class";
  name: string | undefined;
  addInitializer: (initializer: () => void) => void;
}) => Function | void;


const VueComponentOptions: ClassDecorator = (targetClass: Function, context: ClassDecoratorContext): Function | void => {

  // TODO check for the kind === "class"

  const vueOptions: ComponentOptions = {
    methods: {},
    computed: {}
  };

  return defineComponent(vueOptions);

};

Currently, the usage

/* https://stackoverflow.com/questions/75909821/the-classdecorator-type-defined-in-tc39-proposal-decorators-is-not-applicabl */
/* @ts-ignore TS1270 TS decorators are not fully TC39-compatible yet. */
@VueComponentOptions
export default class ReactiveStateExperimentalSample {

}

causes the error

ReactiveState-ExperimentalSample.vue?../../node_modules/ts-loader/index.js??clonedRuleSet-1!../../node_modules/vue-loader/dist/index.js??ruleSet%5B1%5D.rules%5B10%5D.use%5B0%5D:7
Uncaught TypeError: Function expected
    ... ```

The cause is

If any other type of value besides a function is returned, an error will be thrown.

https://github.com/tc39/proposal-decorators#classes

Maybe I need to wrap the defineComponent to function?

const VueComponentOptions: ClassDecorator = (targetClass: Function, context: ClassDecoratorContext): Function | void => {

  const vueOptions: ComponentOptions = {
    methods: {},
    computed: {}
  };

  return (): ReturnType<typeof defineComponent> => defineComponent(vueOptions);

};

This time, there are no the JavaScript runtime error, but the Vue will emit the warning:

runtime-core.esm-bundler.js:170 [Vue warn]: Invalid VNode type: undefined (undefined) at at

and of course, the component will not be rendered.

What I need to do inside VueComponentOptions for the newest decorators API + Vue 3 case?

Please don' recommend the third-party libraries because this question is focused on implementation.

Upvotes: 2

Views: 462

Answers (1)

Dimava
Dimava

Reputation: 10841

  1. You are trying to:
    • Reimplement a newer alternative of vue-class-component: a class that returns ReturnType<typeof defineComponent>, with tc39 decorators

Please correct me if I understood incorrectly.


<script lang="ts">
import { ComponentOptions, defineComponent } from 'vue';
import type { ComponentPublicInstance } from 'vue';

// vue\packages\runtime-core\src\component.ts
interface ClassComponent {
  new (...args: any[]): ComponentPublicInstance
  // here is where you put 
  __vccOpts: ComponentOptions
}

// you need a class saying it implements ComponentPublicInstance for this.$props and other acessors
const Base = class Base {} as any as ClassComponent;


@(function VueComponentOptions(cls) { // inplace to see implecit typings
  cls.__vccOpts = defineComponent({
    name: cls.name,
    // maybe like this or whatever way you want.
    // ClassComponent used a lot of tricks here.
    //   like co
    data: () => new cls(),
    props: ['msg'],
    // whatever else you need
  })
  // return undefined
})
export default class MyComponent extends Base {
  text = 'from class';
  fn() {
    this.text
    this.$props; // has acess to all things in ComponentPublicInstance 
  }
}
</script>

<template>
  <h1>{{ msg }} {{ text }}</h1>
</template>

See an implementation of alike thing for more info, https://github.com/facing-dev/vue-facing-decorator/blob/master/src/index.ts#L29


If you want to be able to use both @Ops class and @Ops({...etc}) class you'll need an (data: object) => ClassDecorator overload


edit: I forgot about __vccOpts, here they are now

Upvotes: 1

Related Questions