To use Vue component Props type parameter for prop type validation feature.
Vue component type has dedicated Props type parameter to represent props:
CombinedVueInstance https://github.com/vuejs/vue/blob/dev/types/vue.d.ts#L64ComponentPublicInstance https://github.com/vuejs/vue-next/blob/master/packages/runtime-core/src/componentPublicInstance.ts#L163By using it, we can validate any kind of component if it is properly typed. An example use case is Vue Class Component. I'm planning to introduce a new way to define component props and it will be quite a different API interface from the basic Vue's prop definition. We can even use prop type validation with class component if we use Props type parameter from the component.
It may also solve #2312 and #2343.
Wow, I mentioned the same method in Discord with @octref .🤣
Here is a POC in TS AST viewer.
https://ts-ast-viewer.com/#code/KYDwDg9gTgLgBDAnmYcAKUJgDwBUB8cAvHAN5wAUAlAFxy5wC+cAPmXAHbADuFAdAICGUAOYBnOlwBuwKAG0AurXpwAZHAgAjAFbAAxvGZtyXXgL7DxdMTCgBLDiMXKAYgFcOBuxA5MAUH6gkLAIyKgYWLhheIQkETgErOiYCfiKANwBQdDwSCjJWABqggA2dgAmgjDQMcQFYADyYDDeHGK1bPFRKDGZfnmoACJVgoPAAGbYwzCCADT1YrFw04JJFBQwABZ2EnAASsCC5T4liNjxi1TEhCtUWeA5cA4wsuOCeuEpTS0+7bhEgg4iEIpD8cFCKAA-HQutECJlwVBgABHNx2JHlaFwTQQCAlQ4cBFwcoTQRuEowLEMNgccklJIeEnjBzAcprajXFQ0ukMjhMlnlKhEqSlCpVaCQigikpuYB0XDKHF4gmZRj3YK5ML7fTQcoXMbMjh2H4cWokUHguQAaSevgA1sBEBBxvQFDCUsUypVqlA8NaFPg-GqAgM4ABhCAAW0gXA4MG+rXaK3mFyWFvBcDAKV2Bz0uv1Ewcxta52z+FmYIz3sEdBWBqmIxTZaDIa1EejPmAcYAkm0Zp5gA2Zk2sIs6is1AsAnm+3BQC8+XUh3MFvgKFgTbt2zGu-HmonlyOwJc6NvOz2+4CPofV5z04jgDA3FBfKRmIIxOGozuLzYr4Pk1XFt1UeJkyQpOcQAXcoKHvTNszoOCMzgcZcUQytkMwiE5TgABlWwHBECssKwpFUXRVk6FsWUMMwtVkMYYiqxGSgriQjMkSfF8yFokjsWEKjtjEPhUIgXiM3oiSgzuIA
We can do that by injecting a fake attribute.
Here's an example.
// vue-editor-bridge
export declare const __veturInjectComponentData = <Data, Props>(instance: ComponentInstance<Data, Props>) => {
return instance as ComponentInstance<Data, Props> & {
__vlsComponentData: {
props: Props & { [other: string]: any },
on: ComponentListeners<ComponentInstance<Data, Props>>;
directives: any[];
}
}
}
// childComponent.vue
export default __veturInjectComponentData({
props: {
foo: {
type: String,
required: true
}
}
})
// parentComponent.vue
import childComponent from './childComponent.vue'
declare const __vlsComponentHelper__app_navbar: {
<T>(
vm: T,
tag: string,
data: childComponent.__vlsComponentData & ThisType<T>,
children: any[]
): any
}
POC: https://ts-ast-viewer.com/#code/KYDwDg9gTgLgBDAnmYcAKUJgDwBUB8cAvHAN5wAUAlAFxy5wC+cAPmXAHbADuFAdAICGUAOYBnOlwBuwKAG0AurXpwAZHAgAjAFbAAxvGZtyXXgL7DxdMTCgBLDiMXKAYgFcOBuxA5MAUH6gkLAIyKgYWLhheIQkETgErOiYCfiKANwBQdDwSCjJWABqggA2dgAmgjDQMcQFYADyYDDeHGK1bPFRKDGZfnmoACJVgoPAAGbYwzCCADT1YrFw04JJFBQwABZ2EnAASsCC5T4liNjxi1TEhCtUWeA5cA4wsuOCeuEpTS0+7bhEgg4iEIpD8cFCKAA-HQutECJlwVBgABHNx2JHlaFwTQQCAlQ4cBFwcoTQRuEowLEMNgccklJIeEnjBzAcprajXFQ0ukMjhMlnlKhEqSlCpVaCQigikpuYB0XDKHF4gmZRj3YK5ML7fTQcoXMbMjh2H4cWokUHguQAaSevgA1sBEBBxvQFDCUsUypVqlA8NaFPg-GqAgM4ABhCAAW0gXA4MG+rXaK3mFyWFvBcDAKV2Bz0uv1Ewcxta52z+FmYIz3sEdBWBqmIxTZaDIa1EejPmAcYAkm0Zp5gA2Zk2sIs6is1AsAnm+3BQC8+XUh3MFvgKFgTbt2zGu-HmonlyOwJc6NvOz2+4CPofV5z04jgDA3FBfKRmIIxOGozuLzYr4Pk1XFt+jbb9zxgAAZHYF1kP400ra1bTgB0nRdXAFCxCgABJgBkON5X9K4iEIQFEFVPoZxsOAAH1qJkJ8oF7XQDDPWMYAnEgb1TCgHD-AdTzAtjez469ANTIiQUrB8GN8Xj+w+OAPy-DshMvAcuLLSd7wzcFaKkEoxFY3cVjobSdJ0rNR3dUctLgOQIC2WRrFsBwRDdRSgSYCtzJ8jQOAElTdygmwu1g7AjN-eSAMbVd8CJXyq3RfQWhkXZSIyKTzOYTK1WDSj4DzQK4zqPTH2fJjkoi9iRgoecu3KChtMs49TMynTxlxVqEp0gY6AAZRcxxvO6h9USS8o6FsWU2ozNUdMYYbwWrSgrjMjMkRksgZp8zRhEm7YxD4DqIG2-x5qDKg7j8QqfxgPg9IMqqVj8IA
That's what's working for me, now hooking them up:
Vue 2:
import Vue from 'vue'
import type { ExtendedVue } from 'vue/types/vue'
const __veturInjectComponentData = <Instance extends Vue, Data, Methods, Computed, Props>(
instance: ExtendedVue<Instance, Data, Methods, Computed, Props>
) => {
return instance as ExtendedVue<Instance, Data, Methods, Computed, Props> & {
__vlsComponentData: {
props: Props & { [other: string]: any }
on: ComponentListeners<ExtendedVue<Instance, Data, Methods, Computed, Props>>
directives: any[]
}
}
}
const comp = __veturInjectComponentData(
Vue.extend({
props: {
foo: {
type: String,
required: true
}
},
data() {
return {
bar: this.foo
}
}
})
)
comp.__vlsComponentData
Vue 3:
import { defineComponent } from 'vue'
import type { Component, ComputedOptions, MethodOptions } from 'vue'
const __veturInjectComponentData = <Props, RawBindings, D, C extends ComputedOptions, M extends MethodOptions>(
instance: Component<Props, RawBindings, D, C, M>
) => {
return instance as Component<Props, RawBindings, D, C, M> & {
__vlsComponentData: {
props: Props & { [other: string]: any }
on: ComponentListeners<Component<Props, RawBindings, D, C, M>>
directives: any[]
}
}
}
const comp = __veturInjectComponentData(
defineComponent({
props: {
foo: {
type: String,
required: true
}
},
data() {
return {
bar: this.foo
}
}
})
)
comp.__vlsComponentData
@ktsn I have a rough prototype at #2422. It seems to have fixed #2312 and #2343, works fine with Vue2/3, but doesn't seem to work with vue-class-component — do you mind taking a look?
@ktsn I have a rough prototype at #2422. It seems to have fixed #2312 and #2343, works fine with Vue2/3, but doesn't seem to work with vue-class-component — do you mind taking a look?
I don't think this PR will solve #2343.
Because this interface and type alias probably not from import statments.
It could be defined directly in the file.
We should just import the component script in and try to pull it out.
and I don't think we actually need to inject an export default for each component.
We can keep it and inject when we use.
In vue2 is call ExtendedVue.
In vue3 is call ComponentPublicInstanceConstructor.
@octref
I have a rough prototype at #2422. It seems to have fixed #2312 and #2343, works fine with Vue2/3, but doesn't seem to work with vue-class-component — do you mind taking a look?
Looks like when we directly look at $props type like the below, it obtains the correct Props type from class component.
export const ${injectComponentDataName} = <Props, RawBindings, D, C extends ComputedOptions, M extends MethodOptions>(
instance: { new (...args: any[]): { $props: Props } }
) => {
return instance as Component<Props, RawBindings, D, C, M> & {
__vlsComponentData: {
props: Props & { [other: string]: any }
on: ComponentListeners<Component<Props, RawBindings, D, C, M>>
directives: any[]
}
}
}
This is the same way how Vue 3 get props type on h function.
Just in case, we have to use the new API Vue.with to properly type props with vue-class-component. https://github.com/vuejs/vue-class-component/issues/465
instance: { new (...args: any[]): { $props: Props } }
in my local test, with this modification this works for me perfectly (vue 3)... 👍
But this issue has been not updated for a while. I'm curious what is the plan for the next ?
export default {
emits: {
click: (e: Event, another: string) => {
return true
}
},
}
this would works nicely with https://github.com/vue-styleguidist/vue-styleguidist/issues/965 in the same style
Thanks again guys, this is plugin is awesome.
What is the status on this?
Would love to see this working :)
Most helpful comment
What is the status on this?
Would love to see this working :)