Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit fd96c63

Browse filesBrowse files
committed
feat: allow to define props by class
1 parent 8bdce38 commit fd96c63
Copy full SHA for fd96c63

File tree

Expand file treeCollapse file tree

14 files changed

+221
-403
lines changed
Filter options
Expand file treeCollapse file tree

14 files changed

+221
-403
lines changed

‎example/babel.config.js

Copy file name to clipboard
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
module.exports = {
22
presets: [['@babel/env', { modules: false }]],
3+
plugins: [
4+
['@babel/proposal-decorators', { legacy: true }],
5+
['@babel/proposal-class-properties', { loose: true }],
6+
],
37
}

‎example/src/App.vue

Copy file name to clipboardExpand all lines: example/src/App.vue
+5-5Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
</template>
1313

1414
<script lang="ts">
15-
import { props } from '../../src'
15+
import { Vue } from '../../src'
1616
17-
const Props = props({
18-
propMessage: String
19-
})
17+
class Props {
18+
propMessage!: string
19+
}
2020
21-
export default class App extends Props {
21+
export default class App extends Vue.props(Props) {
2222
// inital data
2323
msg: number = 123
2424

‎example/tsconfig.json

Copy file name to clipboard
+3-7Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
{
22
"compilerOptions": {
33
"target": "esnext",
4-
"lib": [
5-
"dom",
6-
"esnext"
7-
],
4+
"lib": ["dom", "esnext"],
85
"module": "es2015",
96
"moduleResolution": "node",
107
"experimentalDecorators": true,
8+
"useDefineForClassFields": true,
119
"strict": true,
1210
"noUnusedLocals": true,
1311
"noUnusedParameters": true,
1412
"sourceMap": true
1513
},
16-
"include": [
17-
"./**/*.ts"
18-
]
14+
"include": ["./**/*.ts"]
1915
}

‎example/webpack.config.js

Copy file name to clipboardExpand all lines: example/webpack.config.js
+6-13Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const { VueLoaderPlugin } = require('vue-loader')
2+
const babelConfig = require('./babel.config')
23

34
module.exports = {
45
mode: 'development',
@@ -20,7 +21,10 @@ module.exports = {
2021
test: /\.tsx?$/,
2122
exclude: /node_modules/,
2223
use: [
23-
'babel-loader',
24+
{
25+
loader: 'babel-loader',
26+
options: babelConfig,
27+
},
2428
{
2529
loader: 'ts-loader',
2630
options: {
@@ -32,18 +36,7 @@ module.exports = {
3236
},
3337
{
3438
test: /\.vue$/,
35-
use: [
36-
{
37-
loader: 'vue-loader',
38-
options: {
39-
babelParserPlugins: [
40-
'jsx',
41-
'classProperties',
42-
'decorators-legacy',
43-
],
44-
},
45-
},
46-
],
39+
use: ['vue-loader'],
4740
},
4841
],
4942
},

‎src/helpers.ts

Copy file name to clipboardExpand all lines: src/helpers.ts
+3-76Lines changed: 3 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,6 @@
1-
import {
2-
ComponentOptions,
3-
ComponentObjectPropsOptions,
4-
ExtractPropTypes,
5-
ExtractDefaultPropTypes,
6-
ShallowUnwrapRef,
7-
Ref,
8-
} from 'vue'
1+
import { ComponentOptions, ShallowUnwrapRef, Ref } from 'vue'
92

10-
import {
11-
ClassComponentHooks,
12-
EmitsOptions,
13-
ObjectEmitsOptions,
14-
Vue,
15-
VueBase,
16-
VueConstructor,
17-
VueMixin,
18-
} from './vue'
3+
import { Vue, VueConstructor, VueMixin } from './vue'
194

205
export function Options<V extends Vue>(
216
options: ComponentOptions & ThisType<V>
@@ -63,25 +48,8 @@ export type UnionToIntersection<U> = (
6348

6449
export type ExtractInstance<T> = T extends VueMixin<infer V> ? V : never
6550

66-
export type NarrowEmit<T extends VueBase> = Omit<
67-
T,
68-
'$emit' | keyof ClassComponentHooks
69-
> &
70-
// Reassign class component hooks as mapped types makes prototype function (`mounted(): void`) instance function (`mounted: () => void`).
71-
ClassComponentHooks & {
72-
// Exclude generic $emit type (`$emit: (event: string, ...args: any[]) => void`) if there are another intersected type.
73-
$emit: T['$emit'] extends ((event: string, ...args: any[]) => void) &
74-
infer R
75-
? unknown extends R
76-
? T['$emit']
77-
: R
78-
: T['$emit']
79-
}
80-
8151
export type MixedVueBase<Mixins extends VueMixin[]> = Mixins extends (infer T)[]
82-
? VueConstructor<
83-
NarrowEmit<UnionToIntersection<ExtractInstance<T>> & Vue> & VueBase
84-
>
52+
? VueConstructor<UnionToIntersection<ExtractInstance<T>> & Vue>
8553
: never
8654

8755
export function mixins<T extends VueMixin[]>(...Ctors: T): MixedVueBase<T>
@@ -104,47 +72,6 @@ export function mixins(...Ctors: VueMixin[]): VueConstructor {
10472
}
10573
}
10674

107-
export function props<
108-
PropNames extends string,
109-
Props = Readonly<{ [key in PropNames]?: any }>
110-
>(propNames: PropNames[]): VueConstructor<Vue<Props> & Props>
111-
112-
export function props<
113-
PropsOptions extends ComponentObjectPropsOptions,
114-
Props = Readonly<ExtractPropTypes<PropsOptions>>,
115-
DefaultProps = ExtractDefaultPropTypes<PropsOptions>
116-
>(
117-
propsOptions: PropsOptions
118-
): VueConstructor<Vue<Props, {}, DefaultProps> & Props>
119-
120-
export function props(
121-
propsOptions: string[] | ComponentObjectPropsOptions
122-
): VueConstructor {
123-
class PropsMixin extends Vue {
124-
static __vccExtend(options: ComponentOptions) {
125-
options.props = propsOptions
126-
}
127-
}
128-
return PropsMixin
129-
}
130-
131-
export function emits<EmitNames extends string>(
132-
emitNames: EmitNames[]
133-
): VueConstructor<Vue<unknown, EmitNames[]>>
134-
135-
export function emits<EmitsOptions extends ObjectEmitsOptions>(
136-
emitsOptions: EmitsOptions
137-
): VueConstructor<Vue<unknown, EmitsOptions>>
138-
139-
export function emits(emitsOptions: EmitsOptions): VueConstructor {
140-
class EmitsMixin extends Vue {
141-
static __vccExtend(options: ComponentOptions) {
142-
options.emits = emitsOptions
143-
}
144-
}
145-
return EmitsMixin
146-
}
147-
14875
export type UnwrapSetupValue<T> = T extends Ref<infer R>
14976
? R
15077
: ShallowUnwrapRef<T>

‎src/index.ts

Copy file name to clipboardExpand all lines: src/index.ts
+15-9Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,9 @@
44

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

7-
export {
8-
Options,
9-
createDecorator,
10-
mixins,
11-
props,
12-
emits,
13-
setup,
14-
} from './helpers'
7+
export { Options, createDecorator, mixins, setup } from './helpers'
8+
9+
export { prop } from './props'
1510

1611
/**
1712
* Other types
@@ -27,11 +22,22 @@ export {
2722
PublicProps,
2823
} from './vue'
2924

25+
export {
26+
PropOptions,
27+
PropOptionsWithDefault,
28+
PropOptionsWithRequired,
29+
WithDefault,
30+
VueWithProps,
31+
DefaultFactory,
32+
DefaultKeys,
33+
ExtractDefaultProps,
34+
ExtractProps,
35+
} from './props'
36+
3037
export {
3138
VueDecorator,
3239
MixedVueBase,
3340
UnionToIntersection,
3441
ExtractInstance,
35-
NarrowEmit,
3642
UnwrapSetupValue,
3743
} from './helpers'

‎src/props.ts

Copy file name to clipboard
+56Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { Prop, PropType } from 'vue'
2+
import { Vue } from './vue'
3+
4+
declare const withDefaultSymbol: unique symbol
5+
6+
export interface WithDefault<T> {
7+
[withDefaultSymbol]: T
8+
}
9+
10+
export type DefaultFactory<T> = (
11+
props: Record<string, unknown>
12+
) => T | null | undefined
13+
14+
export interface PropOptions<T = any, D = T> {
15+
type?: PropType<T> | true | null
16+
required?: boolean
17+
default?: D | DefaultFactory<D> | null | undefined | object
18+
validator?(value: unknown): boolean
19+
}
20+
21+
export interface PropOptionsWithDefault<T, D = T> extends PropOptions<T, D> {
22+
default: PropOptions<T, D>['default']
23+
}
24+
25+
export interface PropOptionsWithRequired<T, D = T> extends PropOptions<T, D> {
26+
required: true
27+
}
28+
29+
export type VueWithProps<P> = Vue<ExtractProps<P>, {}, ExtractDefaultProps<P>> &
30+
ExtractProps<P>
31+
32+
export type ExtractProps<P> = {
33+
[K in keyof P]: P[K] extends WithDefault<infer T> ? T : P[K]
34+
}
35+
36+
export type DefaultKeys<P> = {
37+
[K in keyof P]: P[K] extends WithDefault<any> ? K : never
38+
}[keyof P]
39+
40+
export type ExtractDefaultProps<P> = {
41+
[K in DefaultKeys<P>]: P[K] extends WithDefault<infer T> ? T : never
42+
}
43+
44+
// With default
45+
export function prop<T>(options: PropOptionsWithDefault<T>): WithDefault<T>
46+
47+
// With required
48+
export function prop<T>(options: PropOptionsWithRequired<T>): T
49+
50+
// Others
51+
export function prop<T>(options: Prop<T>): T
52+
53+
// Actual implementation
54+
export function prop(options: Prop<unknown>): unknown {
55+
return options
56+
}

‎src/vue.ts

Copy file name to clipboardExpand all lines: src/vue.ts
+31-5Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import {
88
AllowedComponentProps,
99
ComponentCustomProps,
1010
proxyRefs,
11+
Prop,
12+
ComponentObjectPropsOptions,
1113
} from 'vue'
14+
import { VueWithProps } from './props'
1215

1316
function defineGetter<T, K extends keyof T>(
1417
obj: T,
@@ -82,10 +85,6 @@ export interface VueStatic {
8285

8386
/** @internal */
8487
__hmrId?: string
85-
86-
// --- Public APIs
87-
88-
registerHooks(keys: string[]): void
8988
}
9089

9190
export type PublicProps = VNodeProps &
@@ -94,7 +93,9 @@ export type PublicProps = VNodeProps &
9493

9594
export type VueBase = Vue<unknown, never[]>
9695

97-
export type VueMixin<V extends VueBase = VueBase> = VueStatic & { prototype: V }
96+
export type VueMixin<V extends VueBase = VueBase> = VueStatic & {
97+
prototype: V
98+
}
9899

99100
export interface ClassComponentHooks {
100101
// To be extended on user land
@@ -140,6 +141,14 @@ export type Vue<
140141

141142
export interface VueConstructor<V extends VueBase = Vue> extends VueMixin<V> {
142143
new (...args: any[]): V
144+
145+
// --- Public APIs
146+
147+
registerHooks(keys: string[]): void
148+
149+
props<P extends { new (): unknown }>(
150+
Props: P
151+
): VueConstructor<V & VueWithProps<InstanceType<P>>>
143152
}
144153

145154
class VueImpl {
@@ -291,6 +300,23 @@ class VueImpl {
291300
this.__vccHooks.push(...keys)
292301
}
293302

303+
static props(Props: { new (): unknown }): VueConstructor {
304+
const propsMeta = new Props() as Record<string, Prop<any> | undefined>
305+
const props: ComponentObjectPropsOptions = {}
306+
307+
Object.keys(propsMeta).forEach((key) => {
308+
const meta = propsMeta[key]
309+
props[key] = meta ?? null
310+
})
311+
312+
class PropsMixin extends this {
313+
static __vccExtend(options: ComponentOptions) {
314+
options.props = props
315+
}
316+
}
317+
return PropsMixin as VueConstructor
318+
}
319+
294320
$props!: Record<string, any>
295321
$emit!: (event: string, ...args: any[]) => void
296322
$attrs!: ComponentPublicInstance['$attrs']

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.