mirror of https://github.com/vuejs/core.git
feat(types/slots): support slot presence / props type checks via `defineSlots` macro and `slots` option (#7982)
This commit is contained in:
parent
59e828448e
commit
5a2f5d59cf
|
@ -1785,6 +1785,51 @@ return { props, emit }
|
|||
})"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > with TypeScript > defineSlots() > basic usage 1`] = `
|
||||
"import { useSlots as _useSlots, defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
const slots = _useSlots()
|
||||
|
||||
return { slots }
|
||||
}
|
||||
|
||||
})"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > with TypeScript > defineSlots() > w/o generic params 1`] = `
|
||||
"import { useSlots as _useSlots } from 'vue'
|
||||
|
||||
export default {
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
const slots = _useSlots()
|
||||
|
||||
return { slots }
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > with TypeScript > defineSlots() > w/o return value 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
||||
|
||||
return { }
|
||||
}
|
||||
|
||||
})"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > with TypeScript > hoist type declarations 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
export interface Foo {}
|
||||
|
|
|
@ -1585,6 +1585,45 @@ const emit = defineEmits(['a', 'b'])
|
|||
assertCode(content)
|
||||
})
|
||||
|
||||
describe('defineSlots()', () => {
|
||||
test('basic usage', () => {
|
||||
const { content } = compile(`
|
||||
<script setup lang="ts">
|
||||
const slots = defineSlots<{
|
||||
default: { msg: string }
|
||||
}>()
|
||||
</script>
|
||||
`)
|
||||
assertCode(content)
|
||||
expect(content).toMatch(`const slots = _useSlots()`)
|
||||
expect(content).not.toMatch('defineSlots')
|
||||
})
|
||||
|
||||
test('w/o return value', () => {
|
||||
const { content } = compile(`
|
||||
<script setup lang="ts">
|
||||
defineSlots<{
|
||||
default: { msg: string }
|
||||
}>()
|
||||
</script>
|
||||
`)
|
||||
assertCode(content)
|
||||
expect(content).not.toMatch('defineSlots')
|
||||
expect(content).not.toMatch(`_useSlots`)
|
||||
})
|
||||
|
||||
test('w/o generic params', () => {
|
||||
const { content } = compile(`
|
||||
<script setup>
|
||||
const slots = defineSlots()
|
||||
</script>
|
||||
`)
|
||||
assertCode(content)
|
||||
expect(content).toMatch(`const slots = _useSlots()`)
|
||||
expect(content).not.toMatch('defineSlots')
|
||||
})
|
||||
})
|
||||
|
||||
test('runtime Enum', () => {
|
||||
const { content, bindings } = compile(
|
||||
`<script setup lang="ts">
|
||||
|
|
|
@ -67,6 +67,7 @@ const DEFINE_EMITS = 'defineEmits'
|
|||
const DEFINE_EXPOSE = 'defineExpose'
|
||||
const WITH_DEFAULTS = 'withDefaults'
|
||||
const DEFINE_OPTIONS = 'defineOptions'
|
||||
const DEFINE_SLOTS = 'defineSlots'
|
||||
|
||||
const isBuiltInDir = makeMap(
|
||||
`once,memo,if,for,else,else-if,slot,text,html,on,bind,model,show,cloak,is`
|
||||
|
@ -312,6 +313,7 @@ export function compileScript(
|
|||
let hasDefaultExportName = false
|
||||
let hasDefaultExportRender = false
|
||||
let hasDefineOptionsCall = false
|
||||
let hasDefineSlotsCall = false
|
||||
let propsRuntimeDecl: Node | undefined
|
||||
let propsRuntimeDefaults: Node | undefined
|
||||
let propsDestructureDecl: Node | undefined
|
||||
|
@ -590,6 +592,30 @@ export function compileScript(
|
|||
return true
|
||||
}
|
||||
|
||||
function processDefineSlots(node: Node, declId?: LVal): boolean {
|
||||
if (!isCallOf(node, DEFINE_SLOTS)) {
|
||||
return false
|
||||
}
|
||||
if (hasDefineSlotsCall) {
|
||||
error(`duplicate ${DEFINE_SLOTS}() call`, node)
|
||||
}
|
||||
hasDefineSlotsCall = true
|
||||
|
||||
if (node.arguments.length > 0) {
|
||||
error(`${DEFINE_SLOTS}() cannot accept arguments`, node)
|
||||
}
|
||||
|
||||
if (declId) {
|
||||
s.overwrite(
|
||||
startOffset + node.start!,
|
||||
startOffset + node.end!,
|
||||
`${helper('useSlots')}()`
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function getAstBody(): Statement[] {
|
||||
return scriptAst
|
||||
? [...scriptSetupAst.body, ...scriptAst.body]
|
||||
|
@ -683,6 +709,7 @@ export function compileScript(
|
|||
let propsOption = undefined
|
||||
let emitsOption = undefined
|
||||
let exposeOption = undefined
|
||||
let slotsOption = undefined
|
||||
if (optionsRuntimeDecl.type === 'ObjectExpression') {
|
||||
for (const prop of optionsRuntimeDecl.properties) {
|
||||
if (
|
||||
|
@ -692,6 +719,7 @@ export function compileScript(
|
|||
if (prop.key.name === 'props') propsOption = prop
|
||||
if (prop.key.name === 'emits') emitsOption = prop
|
||||
if (prop.key.name === 'expose') exposeOption = prop
|
||||
if (prop.key.name === 'slots') slotsOption = prop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -714,6 +742,12 @@ export function compileScript(
|
|||
exposeOption
|
||||
)
|
||||
}
|
||||
if (slotsOption) {
|
||||
error(
|
||||
`${DEFINE_OPTIONS}() cannot be used to declare slots. Use ${DEFINE_SLOTS}() instead.`,
|
||||
slotsOption
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -1286,7 +1320,8 @@ export function compileScript(
|
|||
processDefineProps(expr) ||
|
||||
processDefineEmits(expr) ||
|
||||
processDefineOptions(expr) ||
|
||||
processWithDefaults(expr)
|
||||
processWithDefaults(expr) ||
|
||||
processDefineSlots(expr)
|
||||
) {
|
||||
s.remove(node.start! + startOffset, node.end! + startOffset)
|
||||
} else if (processDefineExpose(expr)) {
|
||||
|
@ -1320,7 +1355,10 @@ export function compileScript(
|
|||
const isDefineProps =
|
||||
processDefineProps(init, decl.id) ||
|
||||
processWithDefaults(init, decl.id)
|
||||
const isDefineEmits = processDefineEmits(init, decl.id)
|
||||
const isDefineEmits =
|
||||
!isDefineProps && processDefineEmits(init, decl.id)
|
||||
!isDefineEmits && processDefineSlots(init, decl.id)
|
||||
|
||||
if (isDefineProps || isDefineEmits) {
|
||||
if (left === 1) {
|
||||
s.remove(node.start! + startOffset, node.end! + startOffset)
|
||||
|
|
|
@ -8,7 +8,10 @@ import {
|
|||
ComponentPublicInstance,
|
||||
ComponentOptions,
|
||||
SetupContext,
|
||||
h
|
||||
h,
|
||||
SlotsType,
|
||||
Slots,
|
||||
VNode
|
||||
} from 'vue'
|
||||
import { describe, expectType, IsUnion } from './utils'
|
||||
|
||||
|
@ -1406,6 +1409,69 @@ export default {
|
|||
})
|
||||
}
|
||||
|
||||
describe('slots', () => {
|
||||
const comp1 = defineComponent({
|
||||
slots: Object as SlotsType<{
|
||||
default: { foo: string; bar: number }
|
||||
optional?: { data: string }
|
||||
undefinedScope: undefined | { data: string }
|
||||
optionalUndefinedScope?: undefined | { data: string }
|
||||
}>,
|
||||
setup(props, { slots }) {
|
||||
expectType<(scope: { foo: string; bar: number }) => VNode[]>(
|
||||
slots.default
|
||||
)
|
||||
expectType<((scope: { data: string }) => VNode[]) | undefined>(
|
||||
slots.optional
|
||||
)
|
||||
|
||||
slots.default({ foo: 'foo', bar: 1 })
|
||||
|
||||
// @ts-expect-error it's optional
|
||||
slots.optional({ data: 'foo' })
|
||||
slots.optional?.({ data: 'foo' })
|
||||
|
||||
expectType<{
|
||||
(): VNode[]
|
||||
(scope: undefined | { data: string }): VNode[]
|
||||
}>(slots.undefinedScope)
|
||||
|
||||
expectType<
|
||||
| { (): VNode[]; (scope: undefined | { data: string }): VNode[] }
|
||||
| undefined
|
||||
>(slots.optionalUndefinedScope)
|
||||
|
||||
slots.default({ foo: 'foo', bar: 1 })
|
||||
// @ts-expect-error it's optional
|
||||
slots.optional({ data: 'foo' })
|
||||
slots.optional?.({ data: 'foo' })
|
||||
slots.undefinedScope()
|
||||
slots.undefinedScope(undefined)
|
||||
// @ts-expect-error
|
||||
slots.undefinedScope('foo')
|
||||
|
||||
slots.optionalUndefinedScope?.()
|
||||
slots.optionalUndefinedScope?.(undefined)
|
||||
slots.optionalUndefinedScope?.({ data: 'foo' })
|
||||
// @ts-expect-error
|
||||
slots.optionalUndefinedScope()
|
||||
// @ts-expect-error
|
||||
slots.optionalUndefinedScope?.('foo')
|
||||
|
||||
expectType<typeof slots | undefined>(new comp1().$slots)
|
||||
}
|
||||
})
|
||||
|
||||
const comp2 = defineComponent({
|
||||
setup(props, { slots }) {
|
||||
// unknown slots
|
||||
expectType<Slots>(slots)
|
||||
expectType<((...args: any[]) => VNode[]) | undefined>(slots.default)
|
||||
}
|
||||
})
|
||||
expectType<Slots | undefined>(new comp2().$slots)
|
||||
})
|
||||
|
||||
import {
|
||||
DefineComponent,
|
||||
ComponentOptionsMixin,
|
||||
|
@ -1428,6 +1494,7 @@ declare const MyButton: DefineComponent<
|
|||
ComponentOptionsMixin,
|
||||
EmitsOptions,
|
||||
string,
|
||||
{},
|
||||
VNodeProps & AllowedComponentProps & ComponentCustomProps,
|
||||
Readonly<ExtractPropTypes<{}>>,
|
||||
{}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { h, Text, FunctionalComponent, Component } from 'vue'
|
||||
import { h, Text, FunctionalComponent, Component, VNode } from 'vue'
|
||||
import { expectType } from './utils'
|
||||
|
||||
// simple function signature
|
||||
|
@ -68,3 +68,29 @@ const Qux: FunctionalComponent<{}, ['foo', 'bar']> = (props, { emit }) => {
|
|||
}
|
||||
|
||||
expectType<Component>(Qux)
|
||||
|
||||
const Quux: FunctionalComponent<
|
||||
{},
|
||||
{},
|
||||
{
|
||||
default: { foo: number }
|
||||
optional?: { foo: number }
|
||||
}
|
||||
> = (props, { emit, slots }) => {
|
||||
expectType<{
|
||||
default: (scope: { foo: number }) => VNode[]
|
||||
optional?: (scope: { foo: number }) => VNode[]
|
||||
}>(slots)
|
||||
|
||||
slots.default({ foo: 123 })
|
||||
// @ts-expect-error
|
||||
slots.default({ foo: 'fesf' })
|
||||
|
||||
slots.optional?.({ foo: 123 })
|
||||
// @ts-expect-error
|
||||
slots.optional?.({ foo: 'fesf' })
|
||||
// @ts-expect-error
|
||||
slots.optional({ foo: 123 })
|
||||
}
|
||||
expectType<Component>(Quux)
|
||||
;<Quux />
|
||||
|
|
|
@ -4,7 +4,9 @@ import {
|
|||
useAttrs,
|
||||
useSlots,
|
||||
withDefaults,
|
||||
Slots
|
||||
Slots,
|
||||
defineSlots,
|
||||
VNode
|
||||
} from 'vue'
|
||||
import { describe, expectType } from './utils'
|
||||
|
||||
|
@ -179,6 +181,27 @@ describe('defineEmits w/ runtime declaration', () => {
|
|||
emit2('baz')
|
||||
})
|
||||
|
||||
describe('defineSlots', () => {
|
||||
// short syntax
|
||||
const slots = defineSlots<{
|
||||
default: { foo: string; bar: number }
|
||||
optional?: string
|
||||
}>()
|
||||
expectType<(scope: { foo: string; bar: number }) => VNode[]>(slots.default)
|
||||
expectType<undefined | ((scope: string) => VNode[])>(slots.optional)
|
||||
|
||||
// literal fn syntax (allow for specifying return type)
|
||||
const fnSlots = defineSlots<{
|
||||
default(props: { foo: string; bar: number }): any
|
||||
optional?(props: string): any
|
||||
}>()
|
||||
expectType<(scope: { foo: string; bar: number }) => VNode[]>(fnSlots.default)
|
||||
expectType<undefined | ((scope: string) => VNode[])>(fnSlots.optional)
|
||||
|
||||
const slotsUntype = defineSlots()
|
||||
expectType<Slots>(slotsUntype)
|
||||
})
|
||||
|
||||
describe('useAttrs', () => {
|
||||
const attrs = useAttrs()
|
||||
expectType<Record<string, unknown>>(attrs)
|
||||
|
|
|
@ -28,6 +28,7 @@ import {
|
|||
CreateComponentPublicInstance,
|
||||
ComponentPublicInstanceConstructor
|
||||
} from './componentPublicInstance'
|
||||
import { SlotsType } from './componentSlots'
|
||||
|
||||
export type PublicProps = VNodeProps &
|
||||
AllowedComponentProps &
|
||||
|
@ -43,6 +44,7 @@ export type DefineComponent<
|
|||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = {},
|
||||
EE extends string = string,
|
||||
S extends SlotsType = {},
|
||||
PP = PublicProps,
|
||||
Props = Readonly<
|
||||
PropsOrPropOptions extends ComponentPropsOptions
|
||||
|
@ -61,6 +63,7 @@ export type DefineComponent<
|
|||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
S,
|
||||
PP & Props,
|
||||
Defaults,
|
||||
true
|
||||
|
@ -77,6 +80,7 @@ export type DefineComponent<
|
|||
Extends,
|
||||
E,
|
||||
EE,
|
||||
S,
|
||||
Defaults
|
||||
> &
|
||||
PP
|
||||
|
@ -91,29 +95,33 @@ export type DefineComponent<
|
|||
export function defineComponent<
|
||||
Props extends Record<string, any>,
|
||||
E extends EmitsOptions = {},
|
||||
EE extends string = string
|
||||
EE extends string = string,
|
||||
S extends SlotsType = {}
|
||||
>(
|
||||
setup: (
|
||||
props: Props,
|
||||
ctx: SetupContext<E>
|
||||
ctx: SetupContext<E, S>
|
||||
) => RenderFunction | Promise<RenderFunction>,
|
||||
options?: Pick<ComponentOptions, 'name' | 'inheritAttrs'> & {
|
||||
props?: (keyof Props)[]
|
||||
emits?: E | EE[]
|
||||
slots?: S
|
||||
}
|
||||
): (props: Props & EmitsToProps<E>) => any
|
||||
export function defineComponent<
|
||||
Props extends Record<string, any>,
|
||||
E extends EmitsOptions = {},
|
||||
EE extends string = string
|
||||
EE extends string = string,
|
||||
S extends SlotsType = {}
|
||||
>(
|
||||
setup: (
|
||||
props: Props,
|
||||
ctx: SetupContext<E>
|
||||
ctx: SetupContext<E, S>
|
||||
) => RenderFunction | Promise<RenderFunction>,
|
||||
options?: Pick<ComponentOptions, 'name' | 'inheritAttrs'> & {
|
||||
props?: ComponentObjectPropsOptions<Props>
|
||||
emits?: E | EE[]
|
||||
slots?: S
|
||||
}
|
||||
): (props: Props & EmitsToProps<E>) => any
|
||||
|
||||
|
@ -130,6 +138,7 @@ export function defineComponent<
|
|||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = {},
|
||||
EE extends string = string,
|
||||
S extends SlotsType = {},
|
||||
I extends ComponentInjectOptions = {},
|
||||
II extends string = string
|
||||
>(
|
||||
|
@ -143,10 +152,11 @@ export function defineComponent<
|
|||
Extends,
|
||||
E,
|
||||
EE,
|
||||
S,
|
||||
I,
|
||||
II
|
||||
>
|
||||
): DefineComponent<Props, RawBindings, D, C, M, Mixin, Extends, E, EE>
|
||||
): DefineComponent<Props, RawBindings, D, C, M, Mixin, Extends, E, EE, S>
|
||||
|
||||
// overload 3: object format with array props declaration
|
||||
// props inferred as { [key in PropNames]?: any }
|
||||
|
@ -161,6 +171,7 @@ export function defineComponent<
|
|||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = {},
|
||||
EE extends string = string,
|
||||
S extends SlotsType = {},
|
||||
I extends ComponentInjectOptions = {},
|
||||
II extends string = string
|
||||
>(
|
||||
|
@ -174,6 +185,7 @@ export function defineComponent<
|
|||
Extends,
|
||||
E,
|
||||
EE,
|
||||
S,
|
||||
I,
|
||||
II
|
||||
>
|
||||
|
@ -186,7 +198,8 @@ export function defineComponent<
|
|||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
EE
|
||||
EE,
|
||||
S
|
||||
>
|
||||
|
||||
// overload 4: object format with object props declaration
|
||||
|
@ -203,6 +216,7 @@ export function defineComponent<
|
|||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = {},
|
||||
EE extends string = string,
|
||||
S extends SlotsType = {},
|
||||
I extends ComponentInjectOptions = {},
|
||||
II extends string = string
|
||||
>(
|
||||
|
@ -216,10 +230,11 @@ export function defineComponent<
|
|||
Extends,
|
||||
E,
|
||||
EE,
|
||||
S,
|
||||
I,
|
||||
II
|
||||
>
|
||||
): DefineComponent<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>
|
||||
): DefineComponent<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE, S>
|
||||
|
||||
// implementation, close to no-op
|
||||
export function defineComponent(
|
||||
|
|
|
@ -25,6 +25,7 @@ import {
|
|||
ExtractPropTypes
|
||||
} from './componentProps'
|
||||
import { warn } from './warning'
|
||||
import { SlotsType, TypedSlots } from './componentSlots'
|
||||
|
||||
// dev only
|
||||
const warnRuntimeUsage = (method: string) =>
|
||||
|
@ -184,9 +185,7 @@ export function defineOptions<
|
|||
C extends ComputedOptions = {},
|
||||
M extends MethodOptions = {},
|
||||
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = EmitsOptions,
|
||||
EE extends string = string
|
||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin
|
||||
>(
|
||||
options?: ComponentOptionsWithoutProps<
|
||||
{},
|
||||
|
@ -195,16 +194,23 @@ export function defineOptions<
|
|||
C,
|
||||
M,
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
EE
|
||||
> & { emits?: undefined; expose?: undefined }
|
||||
Extends
|
||||
> & { emits?: undefined; expose?: undefined; slots?: undefined }
|
||||
): void {
|
||||
if (__DEV__) {
|
||||
warnRuntimeUsage(`defineOptions`)
|
||||
}
|
||||
}
|
||||
|
||||
export function defineSlots<
|
||||
S extends Record<string, any> = Record<string, any>
|
||||
>(): // @ts-expect-error
|
||||
TypedSlots<SlotsType<S>> {
|
||||
if (__DEV__) {
|
||||
warnRuntimeUsage(`defineSlots`)
|
||||
}
|
||||
}
|
||||
|
||||
type NotUndefined<T> = T extends undefined ? never : T
|
||||
|
||||
type InferDefaults<T> = {
|
||||
|
|
|
@ -27,7 +27,13 @@ import {
|
|||
initProps,
|
||||
normalizePropsOptions
|
||||
} from './componentProps'
|
||||
import { Slots, initSlots, InternalSlots } from './componentSlots'
|
||||
import {
|
||||
initSlots,
|
||||
InternalSlots,
|
||||
Slots,
|
||||
SlotsType,
|
||||
TypedSlots
|
||||
} from './componentSlots'
|
||||
import { warn } from './warning'
|
||||
import { ErrorCodes, callWithErrorHandling, handleError } from './errorHandling'
|
||||
import { AppContext, createAppContext, AppConfig } from './apiCreateApp'
|
||||
|
@ -57,7 +63,8 @@ import {
|
|||
isPromise,
|
||||
ShapeFlags,
|
||||
extend,
|
||||
getGlobalThis
|
||||
getGlobalThis,
|
||||
IfAny
|
||||
} from '@vue/shared'
|
||||
import { SuspenseBoundary } from './components/Suspense'
|
||||
import { CompilerOptions } from '@vue/compiler-core'
|
||||
|
@ -117,12 +124,19 @@ export interface ComponentInternalOptions {
|
|||
__name?: string
|
||||
}
|
||||
|
||||
export interface FunctionalComponent<P = {}, E extends EmitsOptions = {}>
|
||||
extends ComponentInternalOptions {
|
||||
export interface FunctionalComponent<
|
||||
P = {},
|
||||
E extends EmitsOptions = {},
|
||||
S extends Record<string, any> = any
|
||||
> extends ComponentInternalOptions {
|
||||
// use of any here is intentional so it can be a valid JSX Element constructor
|
||||
(props: P, ctx: Omit<SetupContext<E>, 'expose'>): any
|
||||
(
|
||||
props: P,
|
||||
ctx: Omit<SetupContext<E, IfAny<S, {}, SlotsType<S>>>, 'expose'>
|
||||
): any
|
||||
props?: ComponentPropsOptions<P>
|
||||
emits?: E | (keyof E)[]
|
||||
slots?: IfAny<S, Slots, SlotsType<S>>
|
||||
inheritAttrs?: boolean
|
||||
displayName?: string
|
||||
compatConfig?: CompatConfig
|
||||
|
@ -147,7 +161,7 @@ export type ConcreteComponent<
|
|||
M extends MethodOptions = MethodOptions
|
||||
> =
|
||||
| ComponentOptions<Props, RawBindings, D, C, M>
|
||||
| FunctionalComponent<Props, any>
|
||||
| FunctionalComponent<Props, any, any>
|
||||
|
||||
/**
|
||||
* A type used in public APIs where a component type is expected.
|
||||
|
@ -168,10 +182,13 @@ export type { ComponentOptions }
|
|||
type LifecycleHook<TFn = Function> = TFn[] | null
|
||||
|
||||
// use `E extends any` to force evaluating type to fix #2362
|
||||
export type SetupContext<E = EmitsOptions> = E extends any
|
||||
export type SetupContext<
|
||||
E = EmitsOptions,
|
||||
S extends SlotsType = {}
|
||||
> = E extends any
|
||||
? {
|
||||
attrs: Data
|
||||
slots: Slots
|
||||
slots: TypedSlots<S>
|
||||
emit: EmitFn<E>
|
||||
expose: (exposed?: Record<string, any>) => void
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@ import {
|
|||
} from './compat/compatConfig'
|
||||
import { OptionMergeFunction } from './apiCreateApp'
|
||||
import { LifecycleHooks } from './enums'
|
||||
import { SlotsType } from './componentSlots'
|
||||
|
||||
/**
|
||||
* Interface for declaring custom options.
|
||||
|
@ -105,6 +106,7 @@ export interface ComponentOptionsBase<
|
|||
Extends extends ComponentOptionsMixin,
|
||||
E extends EmitsOptions,
|
||||
EE extends string = string,
|
||||
S extends SlotsType = {},
|
||||
Defaults = {},
|
||||
I extends ComponentInjectOptions = {},
|
||||
II extends string = string
|
||||
|
@ -122,7 +124,7 @@ export interface ComponentOptionsBase<
|
|||
>
|
||||
>
|
||||
>,
|
||||
ctx: SetupContext<E>
|
||||
ctx: SetupContext<E, S>
|
||||
) => Promise<RawBindings> | RawBindings | RenderFunction | void
|
||||
name?: string
|
||||
template?: string | object // can be a direct DOM node
|
||||
|
@ -136,6 +138,7 @@ export interface ComponentOptionsBase<
|
|||
directives?: Record<string, Directive>
|
||||
inheritAttrs?: boolean
|
||||
emits?: (E | EE[]) & ThisType<void>
|
||||
slots?: S
|
||||
// TODO infer public instance type based on exposed keys
|
||||
expose?: string[]
|
||||
serverPrefetch?(): void | Promise<any>
|
||||
|
@ -216,6 +219,7 @@ export type ComponentOptionsWithoutProps<
|
|||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = EmitsOptions,
|
||||
EE extends string = string,
|
||||
S extends SlotsType = {},
|
||||
I extends ComponentInjectOptions = {},
|
||||
II extends string = string,
|
||||
PE = Props & EmitsToProps<E>
|
||||
|
@ -229,6 +233,7 @@ export type ComponentOptionsWithoutProps<
|
|||
Extends,
|
||||
E,
|
||||
EE,
|
||||
S,
|
||||
{},
|
||||
I,
|
||||
II
|
||||
|
@ -244,6 +249,7 @@ export type ComponentOptionsWithoutProps<
|
|||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
S,
|
||||
PE,
|
||||
{},
|
||||
false,
|
||||
|
@ -261,6 +267,7 @@ export type ComponentOptionsWithArrayProps<
|
|||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = EmitsOptions,
|
||||
EE extends string = string,
|
||||
S extends SlotsType = {},
|
||||
I extends ComponentInjectOptions = {},
|
||||
II extends string = string,
|
||||
Props = Prettify<Readonly<{ [key in PropNames]?: any } & EmitsToProps<E>>>
|
||||
|
@ -274,6 +281,7 @@ export type ComponentOptionsWithArrayProps<
|
|||
Extends,
|
||||
E,
|
||||
EE,
|
||||
S,
|
||||
{},
|
||||
I,
|
||||
II
|
||||
|
@ -289,6 +297,7 @@ export type ComponentOptionsWithArrayProps<
|
|||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
S,
|
||||
Props,
|
||||
{},
|
||||
false,
|
||||
|
@ -306,6 +315,7 @@ export type ComponentOptionsWithObjectProps<
|
|||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = EmitsOptions,
|
||||
EE extends string = string,
|
||||
S extends SlotsType = {},
|
||||
I extends ComponentInjectOptions = {},
|
||||
II extends string = string,
|
||||
Props = Prettify<Readonly<ExtractPropTypes<PropsOptions> & EmitsToProps<E>>>,
|
||||
|
@ -320,6 +330,7 @@ export type ComponentOptionsWithObjectProps<
|
|||
Extends,
|
||||
E,
|
||||
EE,
|
||||
S,
|
||||
Defaults,
|
||||
I,
|
||||
II
|
||||
|
@ -335,6 +346,7 @@ export type ComponentOptionsWithObjectProps<
|
|||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
S,
|
||||
Props,
|
||||
Defaults,
|
||||
false,
|
||||
|
@ -350,8 +362,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 = any,
|
||||
S extends SlotsType = any
|
||||
> = ComponentOptionsBase<
|
||||
Props,
|
||||
RawBindings,
|
||||
D,
|
||||
C,
|
||||
M,
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
string,
|
||||
S
|
||||
> &
|
||||
ThisType<
|
||||
CreateComponentPublicInstance<
|
||||
{},
|
||||
|
@ -376,6 +400,7 @@ export type ComponentOptionsMixin = ComponentOptionsBase<
|
|||
any,
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
any
|
||||
>
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ import {
|
|||
ComponentInjectOptions
|
||||
} from './componentOptions'
|
||||
import { EmitsOptions, EmitFn } from './componentEmits'
|
||||
import { Slots } from './componentSlots'
|
||||
import { SlotsType, TypedSlots } from './componentSlots'
|
||||
import { markAttrsAccessed } from './componentRenderUtils'
|
||||
import { currentRenderingInstance } from './componentRenderContext'
|
||||
import { warn } from './warning'
|
||||
|
@ -89,6 +89,7 @@ type MixinToOptionTypes<T> = T extends ComponentOptionsBase<
|
|||
infer Extends,
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
infer Defaults
|
||||
>
|
||||
? OptionTypesType<P & {}, B & {}, D & {}, C & {}, M & {}, Defaults & {}> &
|
||||
|
@ -141,6 +142,7 @@ export type CreateComponentPublicInstance<
|
|||
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = {},
|
||||
S extends SlotsType = {},
|
||||
PublicProps = P,
|
||||
Defaults = {},
|
||||
MakeDefaultsOptional extends boolean = false,
|
||||
|
@ -162,10 +164,11 @@ export type CreateComponentPublicInstance<
|
|||
PublicC,
|
||||
PublicM,
|
||||
E,
|
||||
S,
|
||||
PublicProps,
|
||||
PublicDefaults,
|
||||
MakeDefaultsOptional,
|
||||
ComponentOptionsBase<P, B, D, C, M, Mixin, Extends, E, string, Defaults>,
|
||||
ComponentOptionsBase<P, B, D, C, M, Mixin, Extends, E, string, S, Defaults>,
|
||||
I
|
||||
>
|
||||
|
||||
|
@ -178,6 +181,7 @@ export type ComponentPublicInstance<
|
|||
C extends ComputedOptions = {},
|
||||
M extends MethodOptions = {},
|
||||
E extends EmitsOptions = {},
|
||||
S extends SlotsType = {},
|
||||
PublicProps = P,
|
||||
Defaults = {},
|
||||
MakeDefaultsOptional extends boolean = false,
|
||||
|
@ -193,7 +197,7 @@ export type ComponentPublicInstance<
|
|||
>
|
||||
$attrs: Data
|
||||
$refs: Data
|
||||
$slots: Slots
|
||||
$slots: TypedSlots<S>
|
||||
$root: ComponentPublicInstance | null
|
||||
$parent: ComponentPublicInstance | null
|
||||
$emit: EmitFn<E>
|
||||
|
|
|
@ -13,7 +13,9 @@ import {
|
|||
ShapeFlags,
|
||||
extend,
|
||||
def,
|
||||
SlotFlags
|
||||
SlotFlags,
|
||||
Prettify,
|
||||
IfAny
|
||||
} from '@vue/shared'
|
||||
import { warn } from './warning'
|
||||
import { isKeepAlive } from './components/KeepAlive'
|
||||
|
@ -22,7 +24,9 @@ import { isHmrUpdating } from './hmr'
|
|||
import { DeprecationTypes, isCompatEnabled } from './compat/compatConfig'
|
||||
import { toRaw } from '@vue/reactivity'
|
||||
|
||||
export type Slot = (...args: any[]) => VNode[]
|
||||
export type Slot<T extends any = any> = (
|
||||
...args: IfAny<T, any[], [T] | (T extends undefined ? [] : never)>
|
||||
) => VNode[]
|
||||
|
||||
export type InternalSlots = {
|
||||
[name: string]: Slot | undefined
|
||||
|
@ -30,6 +34,24 @@ export type InternalSlots = {
|
|||
|
||||
export type Slots = Readonly<InternalSlots>
|
||||
|
||||
declare const SlotSymbol: unique symbol
|
||||
export type SlotsType<T extends Record<string, any> = Record<string, any>> = {
|
||||
[SlotSymbol]?: T
|
||||
}
|
||||
|
||||
export type TypedSlots<
|
||||
S extends SlotsType,
|
||||
T = NonNullable<S[typeof SlotSymbol]>
|
||||
> = [keyof S] extends [never]
|
||||
? Slots
|
||||
: Readonly<
|
||||
Prettify<{
|
||||
[K in keyof T]: NonNullable<T[K]> extends (...args: any[]) => any
|
||||
? T[K]
|
||||
: Slot<T[K]>
|
||||
}>
|
||||
>
|
||||
|
||||
export type RawSlots = {
|
||||
[name: string]: unknown
|
||||
// manual render fn hint to skip forced children updates
|
||||
|
|
|
@ -120,8 +120,12 @@ export function h(
|
|||
): VNode
|
||||
|
||||
// functional component
|
||||
export function h<P, E extends EmitsOptions = {}>(
|
||||
type: FunctionalComponent<P, E>,
|
||||
export function h<
|
||||
P,
|
||||
E extends EmitsOptions = {},
|
||||
S extends Record<string, any> = {}
|
||||
>(
|
||||
type: FunctionalComponent<P, E, S>,
|
||||
props?: (RawProps & P) | ({} extends P ? null : never),
|
||||
children?: RawChildren | RawSlots
|
||||
): VNode
|
||||
|
|
|
@ -70,6 +70,7 @@ export {
|
|||
defineEmits,
|
||||
defineExpose,
|
||||
defineOptions,
|
||||
defineSlots,
|
||||
withDefaults,
|
||||
// internal
|
||||
mergeDefaults,
|
||||
|
@ -243,7 +244,7 @@ export type {
|
|||
RootRenderFunction
|
||||
} from './renderer'
|
||||
export type { RootHydrateFunction } from './hydration'
|
||||
export type { Slot, Slots } from './componentSlots'
|
||||
export type { Slot, Slots, SlotsType } from './componentSlots'
|
||||
export type {
|
||||
Prop,
|
||||
PropType,
|
||||
|
|
|
@ -4,6 +4,7 @@ type _defineProps = typeof defineProps
|
|||
type _defineEmits = typeof defineEmits
|
||||
type _defineExpose = typeof defineExpose
|
||||
type _defineOptions = typeof defineOptions
|
||||
type _defineSlots = typeof defineSlots
|
||||
type _withDefaults = typeof withDefaults
|
||||
|
||||
declare global {
|
||||
|
@ -11,5 +12,6 @@ declare global {
|
|||
const defineEmits: _defineEmits
|
||||
const defineExpose: _defineExpose
|
||||
const defineOptions: _defineOptions
|
||||
const defineSlots: _defineSlots
|
||||
const withDefaults: _withDefaults
|
||||
}
|
||||
|
|
|
@ -20,7 +20,8 @@ import {
|
|||
warn,
|
||||
ConcreteComponent,
|
||||
ComponentOptions,
|
||||
ComponentInjectOptions
|
||||
ComponentInjectOptions,
|
||||
SlotsType
|
||||
} from '@vue/runtime-core'
|
||||
import { camelize, extend, hyphenate, isArray, toNumber } from '@vue/shared'
|
||||
import { hydrate, render } from '.'
|
||||
|
@ -51,6 +52,7 @@ export function defineCustomElement<
|
|||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = EmitsOptions,
|
||||
EE extends string = string,
|
||||
S extends SlotsType = {},
|
||||
I extends ComponentInjectOptions = {},
|
||||
II extends string = string
|
||||
>(
|
||||
|
@ -64,6 +66,7 @@ export function defineCustomElement<
|
|||
Extends,
|
||||
E,
|
||||
EE,
|
||||
S,
|
||||
I,
|
||||
II
|
||||
> & { styles?: string[] }
|
||||
|
@ -80,6 +83,7 @@ export function defineCustomElement<
|
|||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = Record<string, any>,
|
||||
EE extends string = string,
|
||||
S extends SlotsType = {},
|
||||
I extends ComponentInjectOptions = {},
|
||||
II extends string = string
|
||||
>(
|
||||
|
@ -93,6 +97,7 @@ export function defineCustomElement<
|
|||
Extends,
|
||||
E,
|
||||
EE,
|
||||
S,
|
||||
I,
|
||||
II
|
||||
> & { styles?: string[] }
|
||||
|
@ -109,6 +114,7 @@ export function defineCustomElement<
|
|||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = Record<string, any>,
|
||||
EE extends string = string,
|
||||
S extends SlotsType = {},
|
||||
I extends ComponentInjectOptions = {},
|
||||
II extends string = string
|
||||
>(
|
||||
|
@ -122,6 +128,7 @@ export function defineCustomElement<
|
|||
Extends,
|
||||
E,
|
||||
EE,
|
||||
S,
|
||||
I,
|
||||
II
|
||||
> & { styles?: string[] }
|
||||
|
|
Loading…
Reference in New Issue