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.
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.
ReturnType<typeof defineComponent>
, with tc39 decoratorsPlease 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