Subscribe on changes!

Provide a way to change the type of `$root`

avatar
Dec 8th 2021

What problem does this feature solve?

We use root component as a global store.

createApp({
  data: () => ({ loginInfo: { id: 1, name: 'a' } }),
});

So we can access loginInfo easily in child components

<div>User name: {{$root.loginInfo.name}}</div>

It used to work very well, until we migrated to vue-tsc + volar recently

error: 类型“ComponentPublicInstance<{}, {}, {}, {}, {}, {}, {}, {}, false, ComponentOptionsBase<any, any, any, any, any, any, any, any, any, {}>>”上不存在属性“userInfo”

https://github.com/vuejs/vue-next/blob/d955cfacd64217e381e17d0e1e1fbf476a431080/packages/runtime-core/src/componentPublicInstance.ts#L189

What does the proposed API look like?

$root should be correctly typed as what user specified in createApp. In my case ComponentPublicInstance<{}, {}, { loginInfo:{ id: number; name: string; } }

avatar
Dec 8th 2021

$root and $parent are generally typed as ComponentPublicInstance | null, which makes them almost unusable in vue template. Even any can be a better option than ComponentPublicInstance | null like what we did for $el

avatar
Dec 8th 2021

$parent cannot be typed because it depends on the runtime as a component could be the child of different components at runtime

$root being a type, I don't think you can extend it in your codebase to add properties 🤔 You could however add a custom global property (https://v3.vuejs.org/api/application-config.html#globalproperties) which can be typed by extending the global types. In both scenarios, you will have to manually type your root app as there is no way to directly infer the global type of $root in vue from a createApp() call.

Note you can also do $root as Root in templates by defining a global type Root in one of your global d.ts files type Root = ComponentPublicInstance<{}, {}, { loginInfo:{ id: number; name: string; } }

avatar
Dec 8th 2021

In both scenarios, you will have to manually type your root app as there is no way to directly infer the global type of $root in vue from a createApp() call.

I was aware of it. In addition, there can be multiple createApps in one project.

$el should have been typed as Node, but we type $el as any because we know Node is not useful.

So can we just type $root as any? Using $root as a common ComponentPublicInstance seems meaningless in Vue 3.

While in Vue 2, you may use $root as an event bus.