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 3d9e1f2

Browse filesBrowse files
committed
Merge branch 'props-class' into next
2 parents c3ccae0 + 19880a7 commit 3d9e1f2
Copy full SHA for 3d9e1f2

File tree

Expand file treeCollapse file tree

16 files changed

+288
-466
lines changed
Filter options
Expand file treeCollapse file tree

16 files changed

+288
-466
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
},

‎package.json

Copy file name to clipboardExpand all lines: package.json
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
"@microsoft/api-extractor": "^7.9.9",
5959
"@types/jest": "^26.0.10",
6060
"@types/node": "^14.6.0",
61-
"@vue/compiler-sfc": "^3.0.0-rc.12",
61+
"@vue/compiler-sfc": "^3.0.0",
6262
"babel-jest": "^26.3.0",
6363
"babel-loader": "^8.1.0",
6464
"conventional-changelog-cli": "^2.1.0",
@@ -74,7 +74,7 @@
7474
"ts-loader": "^8.0.2",
7575
"typescript": "^4.0.2",
7676
"uglify-es": "^3.3.9",
77-
"vue": "^3.0.0-rc.12",
77+
"vue": "^3.0.0",
7878
"vue-loader": "^16.0.0-beta.5",
7979
"webpack": "^4.44.1",
8080
"webpack-cli": "^3.3.12"

‎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>
@@ -67,25 +52,8 @@ export type UnionToIntersection<U> = (
6752

6853
export type ExtractInstance<T> = T extends VueMixin<infer V> ? V : never
6954

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

9159
export function mixins<T extends VueMixin[]>(...Ctors: T): MixedVueBase<T>
@@ -108,47 +76,6 @@ export function mixins(...Ctors: VueMixin[]): VueConstructor {
10876
}
10977
}
11078

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

‎src/index.ts

Copy file name to clipboardExpand all lines: src/index.ts
+15-11Lines changed: 15 additions & 11 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
@@ -22,16 +17,25 @@ export {
2217
VueMixin,
2318
VueStatic,
2419
VueConstructor,
25-
EmitsOptions,
26-
ObjectEmitsOptions,
2720
PublicProps,
2821
} from './vue'
2922

23+
export {
24+
PropOptions,
25+
PropOptionsWithDefault,
26+
PropOptionsWithRequired,
27+
WithDefault,
28+
VueWithProps,
29+
DefaultFactory,
30+
DefaultKeys,
31+
ExtractDefaultProps,
32+
ExtractProps,
33+
} from './props'
34+
3035
export {
3136
VueDecorator,
3237
MixedVueBase,
3338
UnionToIntersection,
3439
ExtractInstance,
35-
NarrowEmit,
3640
UnwrapSetupValue,
3741
} 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 | undefined
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
+32-11Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ import {
88
AllowedComponentProps,
99
ComponentCustomProps,
1010
proxyRefs,
11+
Prop,
12+
ComponentObjectPropsOptions,
13+
EmitsOptions,
1114
} from 'vue'
15+
import { VueWithProps } from './props'
1216

1317
function defineGetter<T, K extends keyof T>(
1418
obj: T,
@@ -82,10 +86,6 @@ export interface VueStatic {
8286

8387
/** @internal */
8488
__hmrId?: string
85-
86-
// --- Public APIs
87-
88-
registerHooks(keys: string[]): void
8989
}
9090

9191
export type PublicProps = VNodeProps &
@@ -94,7 +94,9 @@ export type PublicProps = VNodeProps &
9494

9595
export type VueBase = Vue<unknown, never[]>
9696

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

99101
export interface ClassComponentHooks {
100102
// To be extended on user land
@@ -115,12 +117,6 @@ export interface ClassComponentHooks {
115117
serverPrefetch?(): Promise<unknown>
116118
}
117119

118-
export type ObjectEmitsOptions = Record<
119-
string,
120-
((...args: any[]) => any) | null
121-
>
122-
export type EmitsOptions = ObjectEmitsOptions | string[]
123-
124120
export type Vue<
125121
Props = unknown,
126122
Emits extends EmitsOptions = {},
@@ -140,6 +136,14 @@ export type Vue<
140136

141137
export interface VueConstructor<V extends VueBase = Vue> extends VueMixin<V> {
142138
new (...args: any[]): V
139+
140+
// --- Public APIs
141+
142+
registerHooks(keys: string[]): void
143+
144+
props<P extends { new (): unknown }>(
145+
Props: P
146+
): VueConstructor<V & VueWithProps<InstanceType<P>>>
143147
}
144148

145149
class VueImpl {
@@ -291,6 +295,23 @@ class VueImpl {
291295
this.__vccHooks.push(...keys)
292296
}
293297

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

0 commit comments

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