wip: defineOptions -> defineProps + defineEmit + useContext
This commit is contained in:
@@ -1,91 +0,0 @@
|
||||
import { EmitFn, EmitsOptions } from './componentEmits'
|
||||
import { ComponentObjectPropsOptions, ExtractPropTypes } from './componentProps'
|
||||
import { Slots } from './componentSlots'
|
||||
import { Directive } from './directives'
|
||||
import { warn } from './warning'
|
||||
|
||||
interface DefaultContext {
|
||||
props: {}
|
||||
attrs: Record<string, unknown>
|
||||
emit: (...args: any[]) => void
|
||||
slots: Slots
|
||||
}
|
||||
|
||||
interface InferredContext<P, E> {
|
||||
props: Readonly<P>
|
||||
attrs: Record<string, unknown>
|
||||
emit: EmitFn<E>
|
||||
slots: Slots
|
||||
}
|
||||
|
||||
type InferContext<T extends Partial<DefaultContext>, P, E> = {
|
||||
[K in keyof DefaultContext]: T[K] extends {} ? T[K] : InferredContext<P, E>[K]
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a subset of full options that are still useful in the context of
|
||||
* <script setup>. Technically, other options can be used too, but are
|
||||
* discouraged - if using TypeScript, we nudge users away from doing so by
|
||||
* disallowing them in types.
|
||||
*/
|
||||
interface Options<E extends EmitsOptions, EE extends string> {
|
||||
emits?: E | EE[]
|
||||
name?: string
|
||||
inhertiAttrs?: boolean
|
||||
directives?: Record<string, Directive>
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile-time-only helper used for declaring options and retrieving props
|
||||
* and the setup context inside `<script setup>`.
|
||||
* This is stripped away in the compiled code and should never be actually
|
||||
* called at runtime.
|
||||
*/
|
||||
// overload 1: no props
|
||||
export function defineOptions<
|
||||
T extends Partial<DefaultContext> = {},
|
||||
E extends EmitsOptions = EmitsOptions,
|
||||
EE extends string = string
|
||||
>(
|
||||
options?: Options<E, EE> & {
|
||||
props?: undefined
|
||||
}
|
||||
): InferContext<T, {}, E>
|
||||
|
||||
// overload 2: object props
|
||||
export function defineOptions<
|
||||
T extends Partial<DefaultContext> = {},
|
||||
E extends EmitsOptions = EmitsOptions,
|
||||
EE extends string = string,
|
||||
PP extends string = string,
|
||||
P = Readonly<{ [key in PP]?: any }>
|
||||
>(
|
||||
options?: Options<E, EE> & {
|
||||
props?: PP[]
|
||||
}
|
||||
): InferContext<T, P, E>
|
||||
|
||||
// overload 3: object props
|
||||
export function defineOptions<
|
||||
T extends Partial<DefaultContext> = {},
|
||||
E extends EmitsOptions = EmitsOptions,
|
||||
EE extends string = string,
|
||||
PP extends ComponentObjectPropsOptions = ComponentObjectPropsOptions,
|
||||
P = ExtractPropTypes<PP>
|
||||
>(
|
||||
options?: Options<E, EE> & {
|
||||
props?: PP
|
||||
}
|
||||
): InferContext<T, P, E>
|
||||
|
||||
// implementation
|
||||
export function defineOptions() {
|
||||
if (__DEV__) {
|
||||
warn(
|
||||
`defineContext() is a compiler-hint helper that is only usable inside ` +
|
||||
`<script setup> of a single file component. It will be compiled away ` +
|
||||
`and should not be used in final distributed code.`
|
||||
)
|
||||
}
|
||||
return 0 as any
|
||||
}
|
||||
60
packages/runtime-core/src/apiSetupHelpers.ts
Normal file
60
packages/runtime-core/src/apiSetupHelpers.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { shallowReadonly } from '@vue/reactivity'
|
||||
import { getCurrentInstance, SetupContext } from './component'
|
||||
import { EmitFn, EmitsOptions } from './componentEmits'
|
||||
import { ComponentObjectPropsOptions, ExtractPropTypes } from './componentProps'
|
||||
import { warn } from './warning'
|
||||
|
||||
/**
|
||||
* Compile-time-only helper used for declaring props inside `<script setup>`.
|
||||
* This is stripped away in the compiled code and should never be actually
|
||||
* called at runtime.
|
||||
*/
|
||||
// overload 1: string props
|
||||
export function defineProps<
|
||||
TypeProps = undefined,
|
||||
PropNames extends string = string,
|
||||
InferredProps = { [key in PropNames]?: any }
|
||||
>(
|
||||
props?: PropNames[]
|
||||
): Readonly<TypeProps extends undefined ? InferredProps : TypeProps>
|
||||
// overload 2: object props
|
||||
export function defineProps<
|
||||
TypeProps = undefined,
|
||||
PP extends ComponentObjectPropsOptions = ComponentObjectPropsOptions,
|
||||
InferredProps = ExtractPropTypes<PP>
|
||||
>(props?: PP): Readonly<TypeProps extends undefined ? InferredProps : TypeProps>
|
||||
// implementation
|
||||
export function defineProps(props?: any) {
|
||||
if (__DEV__ && props) {
|
||||
warn(
|
||||
`defineProps() is a compiler-hint helper that is only usable inside ` +
|
||||
`<script setup> of a single file component. Its arguments should be ` +
|
||||
`compiled away and passing it at runtime has no effect.`
|
||||
)
|
||||
}
|
||||
return __DEV__
|
||||
? shallowReadonly(getCurrentInstance()!.props)
|
||||
: getCurrentInstance()!.props
|
||||
}
|
||||
|
||||
export function defineEmit<
|
||||
TypeEmit = undefined,
|
||||
E extends EmitsOptions = EmitsOptions,
|
||||
EE extends string = string,
|
||||
InferredEmit = EmitFn<E>
|
||||
>(emitOptions?: E | EE[]): TypeEmit extends undefined ? InferredEmit : TypeEmit
|
||||
// implementation
|
||||
export function defineEmit(emitOptions?: any) {
|
||||
if (__DEV__ && emitOptions) {
|
||||
warn(
|
||||
`defineEmit() is a compiler-hint helper that is only usable inside ` +
|
||||
`<script setup> of a single file component. Its arguments should be ` +
|
||||
`compiled away and passing it at runtime has no effect.`
|
||||
)
|
||||
}
|
||||
return getCurrentInstance()!.emit
|
||||
}
|
||||
|
||||
export function useContext(): SetupContext {
|
||||
return getCurrentInstance()!.setupContext!
|
||||
}
|
||||
@@ -105,7 +105,7 @@ export interface ComponentInternalOptions {
|
||||
export interface FunctionalComponent<P = {}, E extends EmitsOptions = {}>
|
||||
extends ComponentInternalOptions {
|
||||
// use of any here is intentional so it can be a valid JSX Element constructor
|
||||
(props: P, ctx: Omit<SetupContext<E, P>, 'expose'>): any
|
||||
(props: P, ctx: Omit<SetupContext<E>, 'expose'>): any
|
||||
props?: ComponentPropsOptions<P>
|
||||
emits?: E | (keyof E)[]
|
||||
inheritAttrs?: boolean
|
||||
@@ -167,8 +167,7 @@ export const enum LifecycleHooks {
|
||||
ERROR_CAPTURED = 'ec'
|
||||
}
|
||||
|
||||
export interface SetupContext<E = EmitsOptions, P = Data> {
|
||||
props: P
|
||||
export interface SetupContext<E = EmitsOptions> {
|
||||
attrs: Data
|
||||
slots: Slots
|
||||
emit: EmitFn<E>
|
||||
@@ -775,7 +774,6 @@ function createSetupContext(instance: ComponentInternalInstance): SetupContext {
|
||||
})
|
||||
} else {
|
||||
return {
|
||||
props: instance.props,
|
||||
attrs: instance.attrs,
|
||||
slots: instance.slots,
|
||||
emit: instance.emit,
|
||||
|
||||
@@ -98,7 +98,7 @@ export interface ComponentOptionsBase<
|
||||
setup?: (
|
||||
this: void,
|
||||
props: Props,
|
||||
ctx: SetupContext<E, Props>
|
||||
ctx: SetupContext<E>
|
||||
) => Promise<RawBindings> | RawBindings | RenderFunction | void
|
||||
name?: string
|
||||
template?: string | object // can be a direct DOM node
|
||||
|
||||
@@ -95,7 +95,6 @@ export function renderComponentRoot(
|
||||
props,
|
||||
__DEV__
|
||||
? {
|
||||
props,
|
||||
get attrs() {
|
||||
markAttrsAccessed()
|
||||
return attrs
|
||||
@@ -103,7 +102,7 @@ export function renderComponentRoot(
|
||||
slots,
|
||||
emit
|
||||
}
|
||||
: { props, attrs, slots, emit }
|
||||
: { attrs, slots, emit }
|
||||
)
|
||||
: render(props, null as any /* we know it doesn't need it */)
|
||||
)
|
||||
|
||||
@@ -43,7 +43,7 @@ export { provide, inject } from './apiInject'
|
||||
export { nextTick } from './scheduler'
|
||||
export { defineComponent } from './apiDefineComponent'
|
||||
export { defineAsyncComponent } from './apiAsyncComponent'
|
||||
export { defineOptions } from './apiDefineOptions'
|
||||
export { defineProps, defineEmit, useContext } from './apiSetupHelpers'
|
||||
|
||||
// Advanced API ----------------------------------------------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user