Skip to content

Commit 54c27fc

Browse files
authored
feat: support custom class component (#1212)
1 parent a56375f commit 54c27fc

File tree

2 files changed

+102
-3
lines changed

2 files changed

+102
-3
lines changed

src/mount.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import {
2121
ComputedOptions,
2222
ComponentPropsOptions,
2323
ComponentOptions,
24-
ConcreteComponent
24+
ConcreteComponent,
25+
Prop
2526
} from 'vue'
2627

2728
import { MountingOptions, Slot } from './types'
@@ -73,6 +74,25 @@ function getInstanceOptions(
7374
return resultOptions
7475
}
7576

77+
// Class component (without vue-class-component) - no props
78+
export function mount<V>(
79+
originalComponent: {
80+
new (...args: any[]): V
81+
__vccOpts: any
82+
},
83+
options?: MountingOptions<any> & Record<string, any>
84+
): VueWrapper<ComponentPublicInstance<V>>
85+
86+
// Class component (without vue-class-component) - props
87+
export function mount<V, P>(
88+
originalComponent: {
89+
new (...args: any[]): V
90+
__vccOpts: any
91+
defaultProps?: Record<string, Prop<any>> | string[]
92+
},
93+
options?: MountingOptions<P & PublicProps> & Record<string, any>
94+
): VueWrapper<ComponentPublicInstance<V>>
95+
7696
// Class component - no props
7797
export function mount<V>(
7898
originalComponent: {

test-dts/mount.d-test.ts

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
import { expectError, expectType } from './index'
22
import {
3+
ComponentOptions,
34
DefineComponent,
45
defineComponent,
5-
FunctionalComponent
6+
FunctionalComponent,
7+
getCurrentInstance,
8+
h,
9+
ref,
10+
SetupContext,
11+
Prop,
12+
VNodeChild
613
} from 'vue'
714
import { Options, Vue } from 'vue-class-component'
815
import { mount } from '../src'
@@ -179,7 +186,7 @@ declare const FunctionalComponentEmit: FunctionalComponent<
179186
level: number
180187
},
181188
{ hello: (foo: string, bar: string) => void }
182-
>
189+
>
183190

184191
mount(FunctionalComponent)
185192
mount(defineComponent(FunctionalComponent))
@@ -211,6 +218,78 @@ class ClassComponent extends Vue {
211218
expectError(mount(ClassComponent, {}).vm.changeMessage())
212219
mount(ClassComponent, {}).vm.changeMessage('')
213220

221+
// region custom class component implement
222+
class CustomClassComponent<Props extends {} = {}> {
223+
static defaultProps?: Record<string, Prop<any>> | string[]
224+
private static __vccValue?: ComponentOptions
225+
static get __vccOpts(): ComponentOptions {
226+
if (this.__vccValue) return this.__vccValue
227+
const CompConstructor = this
228+
return (this.__vccValue = {
229+
name: CompConstructor.name,
230+
props: CompConstructor.defaultProps,
231+
setup(props, ctx) {
232+
const instance = new CompConstructor()
233+
return instance.render.bind(instance)
234+
}
235+
})
236+
}
237+
constructor() {
238+
const instance = getCurrentInstance()!
239+
this.props = instance.props as Props
240+
// @ts-expect-error no explicit setupContext on instance
241+
this.context = instance.setupContext as SetupContext
242+
}
243+
244+
props: Props
245+
get $props() {
246+
return this.props
247+
}
248+
context: SetupContext
249+
render(): VNodeChild {}
250+
}
251+
class NoPropCustomClassComponent extends CustomClassComponent {
252+
count = ref(0)
253+
changeCount(count: number) {
254+
this.count.value = count
255+
}
256+
render() {
257+
return h('div', `hello world ${this.count.value}`)
258+
}
259+
}
260+
261+
// @ts-expect-error changeCount expects an argument
262+
expectError(mount(NoPropCustomClassComponent, {}).vm.changeCount())
263+
mount(NoPropCustomClassComponent, {}).vm.changeCount(2)
264+
265+
interface CustomClassComponentProps {
266+
size: 'small' | 'large'
267+
age?: number
268+
}
269+
270+
class WithPropCustomClassComponent extends CustomClassComponent<CustomClassComponentProps> {
271+
static defaultProps: (keyof CustomClassComponentProps)[] = ['size', 'age']
272+
count = ref(0)
273+
changeCount(count: number) {
274+
this.count.value = count
275+
}
276+
render() {
277+
return h('div', `hello world ${this.count.value}${this.props.size}`)
278+
}
279+
}
280+
281+
expectError(
282+
// @ts-expect-error should has props error
283+
mount<WithPropCustomClassComponent, CustomClassComponentProps>(WithPropCustomClassComponent, {
284+
props: {}
285+
})
286+
)
287+
mount<WithPropCustomClassComponent, CustomClassComponentProps>(WithPropCustomClassComponent, {
288+
props: { size: 'small' }
289+
})
290+
291+
// endregion
292+
214293
// default props
215294
const Foo = defineComponent({
216295
props: {

0 commit comments

Comments
 (0)