single-spa-vue
single-spa-vue copied to clipboard
Props with Vue3 and Typescript
Hi, I had a go on migrating some of our Vue2 Microfrontends to Vue3. Also, I threw Typescript into the mix.
I'm not sure if it's an issue with the Package itself or just a missing bit of the documentation.
I encountered a problem with the usage of props. The SingleSpaOptsVue3 type doesn't have the props defined to typescript won't compile it. This is of course Typescript exclusive
const vueLifecycles = singleSpaVue({
createApp,
appOptions: {
render() {
return h(App, {
// single-spa props are available on the "this" object. Forward them to your component as needed.
// https://single-spa.js.org/docs/building-applications#lifecyle-props
// if you uncomment these, remember to add matching prop definitions for them in your App.vue file.
/*
name: this.name,
mountParcel: this.mountParcel,
*/
singleSpa: this.singleSpa,
//Property 'singleSpa' does not exist on type 'AppOptions | ((opts: SingleSpaOptsVue2 | SingleSpaOptsVue3, props: object) => Promise<AppOptions>)'.
//Property 'singleSpa' does not exist on type '(opts: SingleSpaOptsVue2 | SingleSpaOptsVue3, props: object) => Promise<AppOptions>'.ts(2339)
});
},
},
});
It can easily be fixed with an this alis (which is kind of ugly and talint doesn't like):
const vueLifecycles = singleSpaVue({
createApp,
appOptions: {
render() {
const thisalias: any = this;
return h(App, {
// single-spa props are available on the "this" object. Forward them to your component as needed.
// https://single-spa.js.org/docs/building-applications#lifecyle-props
// if you uncomment these, remember to add matching prop definitions for them in your App.vue file.
/*
name: this.name,
mountParcel: this.mountParcel,
*/
singleSpa: thisalias.singleSpa,
});
},
},
});
Using appOptions
as an async function works of course (with some types on the props).
const vueLifecycles = singleSpaVue({
createApp,
async appOptions(props: any) {
return {
render: h(App, {
singleSpa: props.singleSpa,
})
}
},
});
Maybe this should be described a bit better in the Docs? Or also improved in the generation? The example Comment suggesting to uncomment the Props is a bit wrong if Typescript is in place.
This sounds like the typescript definitions need to be updated. Here's the file where they can be updated:
https://github.com/single-spa/single-spa-vue/blob/main/types/single-spa-vue.d.ts
The single-spa library has the default props defined at https://github.com/single-spa/single-spa/blob/f67d6fcd404597a246636e302f1d9e7b0f8cd9c6/typings/single-spa.d.ts#L12 - those should be imported into single-spa-vue's types and then used so that this
has those props on them.
Do you have interest in contributing a pull request with the fix?
Yea im interested and will take a closer look the next few days.
Thanks for the initial ressources they look pretty helpfull!
HI @joeldenning sorry for the late response.
I think the props are already handled in https://github.com/single-spa/single-spa-vue/blob/36aa8bbee13da5e47807da7c773b533f4df31377/types/single-spa-vue.d.ts#L6-L10 by the [key: string]: any;
of course you could add the single-spa specific props there too (if wanted I have a PR ready).
But the real issue is the Union in line 15 onwards.
https://github.com/single-spa/single-spa-vue/blob/36aa8bbee13da5e47807da7c773b533f4df31377/types/single-spa-vue.d.ts#L13-L19
This leads understandably to the errors.
Tbh I would recommend updating https://github.com/single-spa/vue-cli-plugin-single-spa/blob/18e92aa559b489128edda8a5675f839e8022a267/template/src/main-vue-3.js#L12-L19 for Typescript configs updating the occurrences of this
(from line 16-18) with a typecast like this: (<AppOptions>this).singleSpa
.
I don't know if this is the preferred way. If yes I would be happy to contribute these changes.
tldr. Proposed Changes:
- [ ] Add SingleSpa specific Props to single-spa-vue
- [ ] Add typescript only typecast to vue3 Template
I think the best solution would be to type the render
function properly. Typecasting with (<AppOptions>this)
is a workaround, but the full solution is to change the function signature.
The first thing to try would be to add the render
function along with the this
type to single-spa-vue:
https://github.com/single-spa/single-spa-vue/blob/36aa8bbee13da5e47807da7c773b533f4df31377/types/single-spa-vue.d.ts#L6-L10
+ import { AppProps } from 'single-spa';
+
type AppOptions = {
el?: string | HTMLElement;
data?: any;
+ render: (this: AppProps) => any;
[key: string]: any;
};
Since this
is caller-dependent, I'm not sure if that will work. If not, you could make a similar change to vue-cli-plugin-single-spa. The this
should be AppProps
, not AppOptions
, since it's the single-spa props that are put on the this
, not the options.
I have exactly the same issue. :(