Skip to content

types(defineComponent): DefineComponent refactor #4465

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
75e447e
feat(types): string type inferrences
pikax Sep 19, 2020
55c6c2c
chore: add h defineComponent
pikax Sep 19, 2020
b99a543
chore: v-model emit update:${props}
pikax Sep 19, 2020
83e9abd
types(defineComponent): Support emit when using functionalComponent
pikax Oct 27, 2020
e0a1656
types(slots): Add typed slots
pikax Nov 30, 2020
b4dc05f
chore: fix tests
pikax Nov 30, 2020
8809f5b
Merge branch 'master-upstream' into types/string_type_inference
pikax Dec 19, 2020
3cb7f02
chore: rename `capitalize` to Capitalize<T>
pikax Dec 19, 2020
341b350
chore: rollback package and typescript dep
pikax Dec 19, 2020
8089a6a
chore: fix build
pikax Dec 19, 2020
415460a
chore: trying to fix tests
pikax Dec 19, 2020
f24b161
chore: defineComponent working as expected with `h`
pikax Dec 19, 2020
8e07103
chore: kinda working but not really
pikax Dec 19, 2020
47de099
chore: ...
pikax Dec 19, 2020
eb87718
Merge branch 'master-upstream' into types/string_type_inference
pikax Jan 26, 2021
748550c
chore: fix test
pikax Jan 26, 2021
e2cac07
types(defineComponent): support for expose component types
pikax Mar 9, 2021
3ccdc0b
chore: add directive typing test
pikax Mar 9, 2021
e140b07
chore: exposed type suppor
pikax Mar 9, 2021
5f49e9c
chore: add global directive type
pikax Mar 9, 2021
fbb62bc
chore: fix tests
pikax Mar 9, 2021
b10bc77
chore: clean dup code
pikax Mar 10, 2021
d0b13fa
chore: add GlobalComponents and GlobalDirectives
pikax Mar 11, 2021
057bad9
chore: add Suspense, KeepAlive, Teleport to GlobalComponents
pikax Mar 13, 2021
9970b45
chore: add Transition and TransitionGroup to globalComponents
pikax Mar 13, 2021
2498929
chore: add BaseTransition ass globalComponent
pikax Mar 13, 2021
b792c6c
chore: add VShow as a globalDirective
pikax Mar 13, 2021
980dbf3
chore: add BaseTransition
pikax Mar 13, 2021
53379a1
Merge branch 'upstream' into feat/typed_slots
pikax Mar 13, 2021
d729fba
chore: added support for `h`
pikax Mar 13, 2021
200838d
chore: correct interface with file name
pikax Mar 14, 2021
dffd7c9
Merge branch 'master' into types/refactor_defineComponent
pikax Mar 30, 2021
08c1217
chore: wrap component to get the type
pikax Mar 30, 2021
ca63ffa
chore: typed directives + VModel directive
pikax Apr 15, 2021
16352ca
chore: move arg after modifiers and add tests
pikax Apr 18, 2021
7754d7e
chore: improve tests
pikax Apr 18, 2021
6558afd
chore: add vOn directive as global
pikax Apr 23, 2021
99741b8
chore: vmodel WIP
pikax Apr 25, 2021
a54a692
Merge branch 'master-upstream' into types/string_type_inference
pikax Jul 16, 2021
0cf01a5
minor: fix test
pikax Jul 16, 2021
675a642
Merge branch 'master-upstream' into types/refactor_defineComponent
pikax Aug 15, 2021
5573d90
chore: remove bad import and fix compat render type
pikax Aug 15, 2021
a3408d7
chore: remove type
pikax Aug 15, 2021
7583477
chore: improve test
pikax Aug 15, 2021
5300104
chore: Merge branch 'master-upstream' into feat/typed_slots
pikax Aug 20, 2021
8a73d1e
chore: Merge branch 'feat/typed_slots' into types/Merge_DefineCompone…
pikax Aug 20, 2021
6fe8330
chore: Merge branch 'types/string_type_inference' into types/Merge_De…
pikax Aug 28, 2021
b3d2b05
chore: Merge branch 'types/definecomponent_functional_emit' into type…
pikax Aug 28, 2021
18a78af
chore: Merge branch 'types/refactor_defineComponent' into types/Merge…
pikax Aug 28, 2021
372eeb2
wip: not properly working
pikax Aug 28, 2021
847d67d
wip: improve mixin emits
pikax Aug 28, 2021
1c4ddad
chore: duplicated must Component types
pikax Aug 29, 2021
e201d2f
chore: defineComponent changes
pikax Aug 29, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions packages/runtime-core/src/apiDefineComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,10 @@ export type DefineComponent<
true
> &
Props
> &
ComponentOptionsBase<
>
/**
* just typescript
*/ & { __isDefineComponent?: true } & ComponentOptionsBase<
Props,
RawBindings,
D,
Expand Down Expand Up @@ -174,7 +176,9 @@ export function defineComponent<
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = Record<string, any>,
EE extends string = string,
S = any
S = any,
Props = Readonly<ExtractPropTypes<PropsOptions>> & EmitsToProps<E>,
Defaults = ExtractDefaultPropTypes<PropsOptions>
>(
options: ComponentOptionsWithObjectProps<
PropsOptions,
Expand All @@ -186,9 +190,25 @@ export function defineComponent<
Extends,
E,
EE,
S
S,
Props,
Defaults
>
): DefineComponent<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>
): DefineComponent<
ExtractPropTypes<PropsOptions>,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
EE,
S,
PublicProps,
Props,
Defaults
>

// implementation, close to no-op
export function defineComponent(options: unknown) {
Expand Down
20 changes: 14 additions & 6 deletions packages/runtime-core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { Directive, validateDirectiveName } from './directives'
import {
applyOptions,
ComponentOptions,
ComponentOptionsMixin,
ComputedOptions,
MethodOptions
} from './componentOptions'
Expand Down Expand Up @@ -109,7 +110,7 @@ export interface FunctionalComponent<
S extends Slots = Slots
> extends ComponentInternalOptions {
// use of any here is intentional so it can be a valid JSX Element constructor
(props: P, ctx: Omit<SetupContext<E, S>, 'expose'>): any
(props: P, ctx: Omit<SetupContext<E, P, S>, 'expose'>): any
props?: ComponentPropsOptions<P>
emits?: E | (keyof E)[]
inheritAttrs?: boolean
Expand All @@ -132,10 +133,13 @@ export type ConcreteComponent<
RawBindings = any,
D = any,
C extends ComputedOptions = ComputedOptions,
M extends MethodOptions = MethodOptions
M extends MethodOptions = MethodOptions,
Mixin extends ComponentOptionsMixin = any,
Extends extends ComponentOptionsMixin = any,
E extends EmitsOptions = any
> =
| ComponentOptions<Props, RawBindings, D, C, M>
| FunctionalComponent<Props, any>
| ComponentOptions<Props, RawBindings, D, C, M, Mixin, Extends, E>
| FunctionalComponent<Props, E>

/**
* A type used in public APIs where a component type is expected.
Expand Down Expand Up @@ -172,10 +176,14 @@ export const enum LifecycleHooks {
SERVER_PREFETCH = 'sp'
}

export interface SetupContext<E = EmitsOptions, S extends Slots = Slots> {
export interface SetupContext<
E = EmitsOptions,
P = {},
S extends Slots = Slots
> {
attrs: Data
slots: S
emit: EmitFn<E>
emit: EmitFn<E, P>
expose: (exposed?: Record<string, any>) => void
}

Expand Down
11 changes: 9 additions & 2 deletions packages/runtime-core/src/componentEmits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,16 @@ export type EmitsToProps<T extends EmitsOptions> = T extends string[]
}
: {}

export type EmitVModelUpdate<
T,
E extends keyof T & string = keyof T & string
> = (event: `update:${E}`, value: T[E]) => void

export type EmitFn<
Options = ObjectEmitsOptions,
P = {},
Event extends keyof Options = keyof Options
> = Options extends Array<infer V>
> = (Options extends Array<infer V>
? (event: V, ...args: any[]) => void
: {} extends Options // if the emit is empty object (usually the default value for emit) should be converted to function
? (event: string, ...args: any[]) => void
Expand All @@ -66,7 +72,8 @@ export type EmitFn<
? (event: key, ...args: Args) => void
: (event: key, ...args: any[]) => void
}[Event]
>
>) &
EmitVModelUpdate<P>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this adding emit('update:foo') for every prop even if they don't have an emit declared for it?


export function emit(
instance: ComponentInternalInstance,
Expand Down
19 changes: 16 additions & 3 deletions packages/runtime-core/src/componentOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export interface ComponentOptionsBase<
UnionToIntersection<ExtractOptionProp<Extends>>
>
>,
ctx: SetupContext<E, Slots<S>>
ctx: SetupContext<E, Props, Slots<S>>
) => Promise<RawBindings> | RawBindings | RenderFunction | void
name?: string
template?: string | object // can be a direct DOM node
Expand Down Expand Up @@ -348,8 +348,20 @@ export type ComponentOptions<
M extends MethodOptions = any,
Mixin extends ComponentOptionsMixin = any,
Extends extends ComponentOptionsMixin = any,
E extends EmitsOptions = any
> = ComponentOptionsBase<Props, RawBindings, D, C, M, Mixin, Extends, E> &
E extends EmitsOptions = {},
S = any
> = ComponentOptionsBase<
Props,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
any,
S
> &
ThisType<
CreateComponentPublicInstance<
{},
Expand All @@ -360,6 +372,7 @@ export type ComponentOptions<
Mixin,
Extends,
E,
any,
Readonly<Props>
>
>
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-core/src/componentProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ type DefaultKeys<T> = {
: never
}[keyof T]

type InferPropType<T> = [T] extends [null]
export type InferPropType<T> = [T] extends [null]
? any // null & true would fail to infer
: [T] extends [{ type: null | true }]
? any // As TS issue https://github.com/Microsoft/TypeScript/issues/14829 // somehow `ObjectConstructor` when inferred from { (): T } becomes `any` // `BooleanConstructor` when inferred from PropConstructor(with PropMethod) becomes `Boolean`
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-core/src/componentPublicInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export type ComponentPublicInstance<
$slots: Slots<S>
$root: ComponentPublicInstance | null
$parent: ComponentPublicInstance | null
$emit: EmitFn<E>
$emit: EmitFn<E, P>
$el: any
$options: Options & MergedComponentOptionsOverride
$forceUpdate: () => void
Expand Down
132 changes: 104 additions & 28 deletions packages/runtime-core/src/h.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ import { RawSlots } from './componentSlots'
import {
FunctionalComponent,
Component,
ComponentOptions,
ConcreteComponent
ConcreteComponent,
ComponentOptions
} from './component'
import { EmitsOptions } from './componentEmits'
import { DefineComponent } from './apiDefineComponent'
import { Slots } from 'test-dts'

// `h` is a more user-friendly version of `createVNode` that allows omitting the
// props when possible. It is intended for manually written render functions.
Expand Down Expand Up @@ -68,16 +69,61 @@ type RawChildren =
| (() => any)

// fake constructor type returned from `defineComponent`
interface Constructor<P = any, S = any> {
interface Constructor<P = {}, S = any> {
__isFragment?: never
__isTeleport?: never
__isSuspense?: never
__isDefineComponent?: never
new (...args: any[]): { $props: P; $slots?: S }
}

// Converts emits value to object
type ExtractEmitEvents<T> = T extends Readonly<Array<infer V>>
? { [K in V & string as `on${Capitalize<K>}`]: (...args: any[]) => void }
: T extends any[]
? { [K in T & string as `on${Capitalize<K>}`]: (...args: any[]) => void }
: {} extends T // if the emit is empty object (usually the default value for emit) should be converted to function
? {}
: {
[K in keyof T & string as `on${Capitalize<K>}`]: T[K] extends (
...args: infer Args
) => any
? (...args: Args) => void
: (...args: any[]) => void
}

type ExtractEmitPropUpdate<
P = {},
PK extends keyof P & string = keyof P & string
> = P extends Readonly<Array<infer V>>
? { [K in V & string as `onUpdate:${K}`]?: (value: any) => void }
: P extends any[]
? { [K in P & string as `onUpdate:${K}`]?: (value: any) => void }
: // we need to omit if it infers emit as props
{
[K in keyof Omit<P, `on${Capitalize<PK>}`> &
string as `onUpdate:${K}`]?: (value: P[K]) => void
}

type RenderProps<P, E extends EmitsOptions = {}> =
| (Partial<ExtractEmitEvents<E>> & RawProps & P & ExtractEmitPropUpdate<P>)
| ({} extends P ? Partial<ExtractEmitEvents<E>> | null : never)

// The following is a series of overloads for providing props validation of
// manually written render functions.

// functional component
// NOTE: is set on top to allow infer the props when doing
/// const Func = (_props: { foo: string; bar?: number }) => ''
/// h(Func, {})
// otherwise it will default to `h(type: string)`
export function h<P, E extends EmitsOptions = {}>(
type: FunctionalComponent<P, E>,
props?: RenderProps<P, E>,
children?: RawChildren | RawSlots
): VNode
export function h(type: FunctionalComponent): VNode

// element
export function h(type: string, children?: RawChildren): VNode
export function h(
Expand Down Expand Up @@ -120,23 +166,24 @@ export function h(
): VNode

// functional component
export function h<P, E extends EmitsOptions = {}>(
type: FunctionalComponent<P, E>,
props?: (RawProps & P) | ({} extends P ? null : never),
children?: RawChildren | RawSlots
): VNode
// export function h<P, E extends EmitsOptions = {}>(
// type: FunctionalComponent<P, E>,
// props?: (RawProps & P) | ({} extends P ? null : never),
// children?: RawChildren | RawSlots
// ): VNode

// catch-all for generic component types
export function h(type: Component, children?: RawChildren): VNode

// concrete component
export function h<P>(
type: ConcreteComponent | string,
export function h<P, E extends EmitsOptions = {}>(
type: ConcreteComponent<P, any, any, any, any, any, any, E> | string,
props?: RenderProps<P, E>,
children?: RawChildren
): VNode

export function h<P>(
type: ConcreteComponent<P> | string,
props?: (RawProps & P) | ({} extends P ? null : never),
type: ConcreteComponent | string,
children?: RawChildren
): VNode

Expand All @@ -148,27 +195,56 @@ export function h(
): VNode

// exclude `defineComponent` constructors
export function h<P>(
type: ComponentOptions<P>,
props?: (RawProps & P) | ({} extends P ? null : never),
children?: RawChildren | RawSlots
): VNode

// fake constructor type returned by `defineComponent` or class component
export function h(type: Constructor, children?: RawChildren): VNode
export function h<P, S>(
type: Constructor<P, S>,
props?: (RawProps & P) | ({} extends P ? null : never),
children?: (RawChildren & S) | ({} extends S ? RawSlots : S)
export function h<
P,
S,
E extends EmitsOptions = {},
PP = {},
Props = {},
Defaults = {}
>(
type: ComponentOptions<P, any, any, any, any, any, any, E, S>,
props?: RenderProps<Partial<Defaults> & Omit<Props & PP, keyof Defaults>, E>,
children?: (RawChildren & Slots<S>) | ({} extends S ? RawSlots : Slots<S>)
): VNode

// fake constructor type returned by `defineComponent`
export function h<
P,
S,
E extends EmitsOptions = {},
PP = {},
Props = {},
Defaults = {}
>(
type: DefineComponent<
P,
any,
any,
any,
any,
any,
any,
E,
any,
S,
PP,
Props,
Defaults
>,
props?: RenderProps<Partial<Defaults> & Omit<Props & PP, keyof Defaults>, E>,
children?: (RawChildren & Slots<S>) | ({} extends S ? RawSlots : Slots<S>)
): VNode
export function h(type: DefineComponent): VNode
export function h(type: DefineComponent, children?: RawChildren): VNode
export function h<P>(
type: DefineComponent<P>,
props?: (RawProps & P) | ({} extends P ? null : never),
children?: RawChildren | RawSlots

// fake constructor type returned by `defineComponent` or class component
export function h<P, S, E extends EmitsOptions = {}>(
type: Constructor<P, S>,
props?: RenderProps<P, E>,
children?: (RawChildren & Slots<S>) | ({} extends S ? RawSlots : Slots<S>)
): VNode
export function h(type: Constructor, children?: RawChildren): VNode

// Actual implementation
export function h(type: any, propsOrChildren?: any, children?: any): VNode {
Expand Down
Loading