Skip to content

Commit ce61b0c

Browse files
committed
feat: add props helper function
1 parent c635ed2 commit ce61b0c

File tree

4 files changed

+60
-17
lines changed

4 files changed

+60
-17
lines changed

src/helpers.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ComponentOptions, SetupContext, UnwrapRef } from 'vue'
1+
import { ComponentOptions, SetupContext, UnwrapRef, ComponentObjectPropsOptions, ExtractPropTypes } from 'vue'
22
import { Vue, VueBase, VueMixin } from './vue'
33

44
export function Options<V extends Vue>(
@@ -58,7 +58,9 @@ export type MixedVueBase<Mixins extends VueMixin[]> = Mixins extends (infer T)[]
5858
export function mixins<T extends VueMixin[]>(...Ctors: T): MixedVueBase<T>
5959
export function mixins(...Ctors: VueMixin[]): VueBase {
6060
return class MixedVue<Props> extends Vue<Props> {
61-
static __vccMixins = Ctors.map((Ctor) => Ctor.__vccOpts)
61+
static __vccExtend(options: ComponentOptions) {
62+
Ctors.forEach((Ctor) => Ctor.__vccExtend(options))
63+
}
6264

6365
constructor(props: Props, ctx: SetupContext) {
6466
super(props, ctx)
@@ -73,6 +75,17 @@ export function mixins(...Ctors: VueMixin[]): VueBase {
7375
}
7476
}
7577

78+
export function props<PropNames extends string, Props = Readonly<{ [key in PropNames]?: any }>>(propNames: PropNames[]): VueBase<Vue<Props> & Props>
79+
export function props<PropsOptions extends ComponentObjectPropsOptions, Props = Readonly<ExtractPropTypes<PropsOptions>>>(propsOptions: PropsOptions): VueBase<Vue<Props> & Props>
80+
export function props(propsOptions: string[] | ComponentObjectPropsOptions): VueBase {
81+
class PropsMixin<Props> extends Vue<Props> {
82+
static __vccExtend(options: ComponentOptions) {
83+
options.props = propsOptions
84+
}
85+
}
86+
return PropsMixin
87+
}
88+
7689
export function setup<R>(setupFn: () => R): UnwrapRef<R> {
7790
// Hack to delay the invocation of setup function.
7891
// Will be called after dealing with class properties.

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
export { Vue, ClassComponentHooks } from './vue'
66

7-
export { Options, createDecorator, mixins, setup } from './helpers'
7+
export { Options, createDecorator, mixins, props, setup } from './helpers'
88

99
/**
1010
* Other types

src/vue.ts

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,13 @@ function defineProxy(proxy: any, key: string, target: any): void {
2929
})
3030
}
3131

32-
function getSuperOptions(Ctor: Function): ComponentOptions | undefined {
32+
function getSuper(Ctor: typeof VueImpl): typeof VueImpl | undefined {
3333
const superProto = Object.getPrototypeOf(Ctor.prototype)
3434
if (!superProto) {
3535
return undefined
3636
}
3737

38-
const Super = superProto.constructor as typeof Vue
39-
return Super.__vccOpts
38+
return superProto.constructor as typeof VueImpl
4039
}
4140

4241
export interface VueStatic {
@@ -52,7 +51,7 @@ export interface VueStatic {
5251
__vccDecorators?: ((options: ComponentOptions) => void)[]
5352

5453
/** @internal */
55-
__vccMixins?: ComponentOptions[]
54+
__vccExtend: ((options: ComponentOptions) => void)
5655

5756
/** @internal */
5857
__vccHooks: string[]
@@ -134,9 +133,6 @@ class VueImpl {
134133
/** @internal */
135134
static __vccDecorators?: ((options: ComponentOptions) => void)[]
136135

137-
/** @internal */
138-
static __vccMixins?: ComponentOptions[]
139-
140136
/** @internal */
141137
static __vccHooks = [
142138
'data',
@@ -155,6 +151,12 @@ class VueImpl {
155151
'serverPrefetch',
156152
]
157153

154+
/** @internal */
155+
static __vccExtend(options: ComponentOptions) {
156+
options.mixins = options.mixins || []
157+
options.mixins.push(this.__vccOpts)
158+
}
159+
158160
/** @internal */
159161
static get __vccOpts(): ComponentOptions {
160162
// Early return if `this` is base class as it does not have any options
@@ -175,12 +177,9 @@ class VueImpl {
175177
: {})
176178

177179
// Handle super class options
178-
options.extends = getSuperOptions(Ctor)
179-
180-
// Handle mixins
181-
const mixins = this.hasOwnProperty('__vccMixins') && this.__vccMixins
182-
if (mixins) {
183-
options.mixins = options.mixins ? options.mixins.concat(mixins) : mixins
180+
const Super = getSuper(Ctor)
181+
if (Super) {
182+
Super.__vccExtend(options)
184183
}
185184

186185
options.methods = { ...options.methods }

test/specs/test.spec.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import 'reflect-metadata'
22
import { h, resolveComponent, ref, onMounted, Ref, watch, toRef } from 'vue'
3-
import { Options, createDecorator, mixins, Vue, setup } from '../../src'
3+
import { Options, createDecorator, mixins, Vue, setup, props } from '../../src'
44
import { mount, unmount } from '../helpers'
55

66
describe('vue-class-component', () => {
@@ -378,6 +378,37 @@ describe('vue-class-component', () => {
378378
expect(root.valueB).toBe(456)
379379
})
380380

381+
it('props mixin: prop names', () => {
382+
const Props = props(['foo', 'bar'])
383+
384+
class App extends Props {
385+
baz = this.foo + this.bar
386+
}
387+
388+
const { root } = mount(App, { foo: 'Hello', bar: 'World' })
389+
expect(root.baz).toBe('HelloWorld')
390+
})
391+
392+
it('props mixin: props options object', () => {
393+
const Props = props({
394+
foo: {
395+
type: String,
396+
default: 'The answer is'
397+
},
398+
bar: {
399+
type: Number,
400+
required: true
401+
}
402+
})
403+
404+
class App extends Props {
405+
baz = this.foo + ': ' + this.bar
406+
}
407+
408+
const { root } = mount(App, { bar: 42 })
409+
expect(root.baz).toBe('The answer is: 42')
410+
})
411+
381412
it('uses composition functions', () => {
382413
function useCounter() {
383414
const count = ref(0)

0 commit comments

Comments
 (0)