wip: defineOptions -> defineProps + defineEmit + useContext

This commit is contained in:
Evan You
2020-11-24 15:12:59 -05:00
parent ae2caad740
commit 47d73c23e1
13 changed files with 593 additions and 523 deletions

View File

@@ -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
}

View 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!
}

View File

@@ -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,

View File

@@ -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

View File

@@ -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 */)
)

View File

@@ -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 ----------------------------------------------------------------