Skip to content

Commit 9047e4a

Browse files
committed
fix(type): improve defineComponent type for option apis
1 parent 56332c1 commit 9047e4a

File tree

7 files changed

+229
-130
lines changed

7 files changed

+229
-130
lines changed

src/component/common.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type Data = { [key: string]: unknown }

src/component/component.ts

Lines changed: 0 additions & 121 deletions
This file was deleted.

src/component/componentOptions.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { Data } from './common'
2+
import { ComponentPropsOptions, ExtractPropTypes } from './componentProps'
3+
import { VNode } from 'vue'
4+
import {
5+
ComponentInstance,
6+
VueProxy,
7+
ComponentRenderProxy,
8+
} from './componentProxy'
9+
10+
import { ComponentOptions as Vue2ComponentOptions } from 'vue'
11+
12+
export interface SetupContext {
13+
readonly attrs: Record<string, string>
14+
readonly slots: { [key: string]: (...args: any[]) => VNode[] }
15+
readonly parent: ComponentInstance | null
16+
readonly root: ComponentInstance
17+
readonly listeners: { [key: string]: Function }
18+
19+
emit(event: string, ...args: any[]): void
20+
}
21+
22+
export type ComputedGetter<T> = (ctx?: any) => T
23+
export type ComputedSetter<T> = (v: T) => void
24+
25+
export interface WritableComputedOptions<T> {
26+
get: ComputedGetter<T>
27+
set: ComputedSetter<T>
28+
}
29+
30+
export type ComputedOptions = Record<
31+
string,
32+
ComputedGetter<any> | WritableComputedOptions<any>
33+
>
34+
35+
export interface MethodOptions {
36+
[key: string]: Function
37+
}
38+
39+
export type SetupFunction<Props, RawBindings> = (
40+
this: void,
41+
props: Props,
42+
ctx: SetupContext
43+
) => RawBindings | (() => VNode | null)
44+
45+
interface ComponentOptionsBase<
46+
Props,
47+
D = Data,
48+
C extends ComputedOptions = {},
49+
M extends MethodOptions = {}
50+
>
51+
extends Omit<
52+
Vue2ComponentOptions<Vue, D, M, C, Props>,
53+
'data' | 'computed' | 'method' | 'setup' | 'props'
54+
> {
55+
data?: D | (() => D)
56+
computed?: C
57+
methods?: M
58+
}
59+
60+
export type ExtractComputedReturns<T extends any> = {
61+
[key in keyof T]: T[key] extends { get: (...args: any[]) => infer TReturn }
62+
? TReturn
63+
: T[key] extends (...args: any[]) => infer TReturn
64+
? TReturn
65+
: never
66+
}
67+
68+
export type ComponentOptionsWithProps<
69+
PropsOptions = ComponentPropsOptions,
70+
RawBindings = Data,
71+
D = Data,
72+
C extends ComputedOptions = {},
73+
M extends MethodOptions = {},
74+
Props = ExtractPropTypes<PropsOptions>
75+
> = ComponentOptionsBase<Props, D, C, M> & {
76+
props?: PropsOptions
77+
setup?: SetupFunction<Props, RawBindings>
78+
} & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>
79+
80+
export type ComponentOptionsWithArrayProps<
81+
PropNames extends string = string,
82+
RawBindings = Data,
83+
D = Data,
84+
C extends ComputedOptions = {},
85+
M extends MethodOptions = {},
86+
Props = Readonly<{ [key in PropNames]?: any }>
87+
> = ComponentOptionsBase<Props, D, C, M> & {
88+
props?: PropNames[]
89+
setup?: SetupFunction<Props, RawBindings>
90+
} & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>
91+
92+
export type ComponentOptionsWithoutProps<
93+
Props = unknown,
94+
RawBindings = Data,
95+
D = Data,
96+
C extends ComputedOptions = {},
97+
M extends MethodOptions = {}
98+
> = ComponentOptionsBase<Props, D, C, M> & {
99+
props?: undefined
100+
setup?: SetupFunction<Props, RawBindings>
101+
} & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>
102+
103+
export type WithLegacyAPI<T, D, C, M, Props> = T &
104+
Omit<Vue2ComponentOptions<Vue, D, M, C, Props>, keyof T>

src/component/componentProps.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Data } from './component'
1+
import { Data } from './common'
22

33
export type ComponentPropsOptions<P = Data> =
44
| ComponentObjectPropsOptions<P>

src/component/componentProxy.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { ExtractPropTypes } from './componentProps'
2+
import { UnwrapRef } from '..'
3+
import { Data } from './common'
4+
5+
import Vue, {
6+
VueConstructor,
7+
ComponentOptions as Vue2ComponentOptions,
8+
} from 'vue'
9+
import {
10+
ComputedOptions,
11+
MethodOptions,
12+
ExtractComputedReturns,
13+
} from './componentOptions'
14+
15+
export type ComponentInstance = InstanceType<VueConstructor>
16+
17+
// public properties exposed on the proxy, which is used as the render context
18+
// in templates (as `this` in the render option)
19+
export type ComponentRenderProxy<
20+
P = {}, // props type extracted from props option
21+
B = {}, // raw bindings returned from setup()
22+
D = {}, // return from data()
23+
C extends ComputedOptions = {},
24+
M extends MethodOptions = {},
25+
PublicProps = P
26+
> = {
27+
$data: D
28+
$props: P & PublicProps
29+
$attrs: Data
30+
$refs: Data
31+
$slots: Data
32+
$root: ComponentInstance | null
33+
$parent: ComponentInstance | null
34+
$emit: (event: string, ...args: unknown[]) => void
35+
} & P &
36+
UnwrapRef<B> &
37+
D &
38+
M &
39+
ExtractComputedReturns<C>
40+
41+
// for Vetur and TSX support
42+
type VueConstructorProxy<PropsOptions, RawBindings> = VueConstructor & {
43+
new (...args: any[]): ComponentRenderProxy<
44+
ExtractPropTypes<PropsOptions>,
45+
UnwrapRef<RawBindings>,
46+
ExtractPropTypes<PropsOptions, false>
47+
>
48+
}
49+
50+
type DefaultData<V> = object | ((this: V) => object)
51+
type DefaultMethods<V> = { [key: string]: (this: V, ...args: any[]) => any }
52+
type DefaultComputed = { [key: string]: any }
53+
54+
export type VueProxy<
55+
PropsOptions,
56+
RawBindings,
57+
Data = DefaultData<Vue>,
58+
Methods = DefaultMethods<Vue>,
59+
Computed = DefaultComputed
60+
> = Vue2ComponentOptions<
61+
Vue,
62+
UnwrapRef<RawBindings> & Data,
63+
Methods,
64+
Computed,
65+
PropsOptions,
66+
ExtractPropTypes<PropsOptions, false>
67+
> &
68+
VueConstructorProxy<PropsOptions, RawBindings>

src/component/defineComponent.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { ComponentPropsOptions } from './componentProps'
2+
import {
3+
MethodOptions,
4+
ComputedOptions,
5+
ComponentOptionsWithoutProps,
6+
ComponentOptionsWithArrayProps,
7+
ComponentOptionsWithProps,
8+
} from './componentOptions'
9+
import { VueProxy } from './componentProxy'
10+
import { Data } from './common'
11+
12+
// overload 1: object format with no props
13+
export function defineComponent<
14+
RawBindings,
15+
D = Data,
16+
C extends ComputedOptions = {},
17+
M extends MethodOptions = {}
18+
>(
19+
options: ComponentOptionsWithoutProps<unknown, RawBindings, D, C, M>
20+
): VueProxy<unknown, RawBindings, D, C, M>
21+
22+
// overload 2: object format with array props declaration
23+
// props inferred as { [key in PropNames]?: any }
24+
// return type is for Vetur and TSX support
25+
export function defineComponent<
26+
PropNames extends string,
27+
RawBindings = Data,
28+
D = Data,
29+
C extends ComputedOptions = {},
30+
M extends MethodOptions = {},
31+
PropsOptions extends ComponentPropsOptions = ComponentPropsOptions
32+
>(
33+
options: ComponentOptionsWithArrayProps<PropNames, RawBindings, D, C, M>
34+
): VueProxy<Readonly<{ [key in PropNames]?: any }>, RawBindings, D, C, M>
35+
36+
// overload 3: object format with object props declaration
37+
// see `ExtractPropTypes` in ./componentProps.ts
38+
export function defineComponent<
39+
Props,
40+
RawBindings = Data,
41+
D = Data,
42+
C extends ComputedOptions = {},
43+
M extends MethodOptions = {},
44+
PropsOptions extends ComponentPropsOptions = ComponentPropsOptions
45+
>(
46+
options: ComponentOptionsWithProps<PropsOptions, RawBindings, D, C, M>
47+
): VueProxy<PropsOptions, RawBindings, D, C, M>
48+
// implementation, close to no-op
49+
export function defineComponent(options: any) {
50+
return options as any
51+
}

src/component/index.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
export {
2-
Data,
3-
defineComponent,
4-
SetupFunction,
5-
SetupContext,
6-
ComponentInstance,
7-
ComponentRenderProxy,
8-
} from './component'
1+
export { defineComponent } from './defineComponent'
2+
export { SetupFunction, SetupContext } from './componentOptions'
3+
export { ComponentInstance, ComponentRenderProxy } from './componentProxy'
4+
export { Data } from './common'
95
export { PropType, PropOptions } from './componentProps'

0 commit comments

Comments
 (0)