diff --git a/packages/dts-test/defineComponent.test-d.tsx b/packages/dts-test/defineComponent.test-d.tsx index 8a1c19733ac..4de5e873de0 100644 --- a/packages/dts-test/defineComponent.test-d.tsx +++ b/packages/dts-test/defineComponent.test-d.tsx @@ -1191,14 +1191,13 @@ describe('async setup', () => { describe('define attrs', () => { test('define attrs w/ object props', () => { - type CompAttrs = { - bar: number - } const MyComp = defineComponent({ props: { foo: String }, - attrs: Object as AttrsType, + attrs: Object as AttrsType<{ + bar?: number + }>, created() { expectType(this.$attrs.bar) } @@ -1207,12 +1206,11 @@ describe('define attrs', () => { }) test('define attrs w/ array props', () => { - type CompAttrs = { - bar: number - } const MyComp = defineComponent({ props: ['foo'], - attrs: Object as AttrsType, + attrs: Object as AttrsType<{ + bar?: number + }>, created() { expectType(this.$attrs.bar) } @@ -1221,11 +1219,10 @@ describe('define attrs', () => { }) test('define attrs w/ no props', () => { - type CompAttrs = { - bar: number - } const MyComp = defineComponent({ - attrs: Object as AttrsType, + attrs: Object as AttrsType<{ + bar?: number + }>, created() { expectType(this.$attrs.bar) } @@ -1234,9 +1231,6 @@ describe('define attrs', () => { }) test('define attrs w/ composition api', () => { - type CompAttrs = { - bar: number - } const MyComp = defineComponent({ props: { foo: { @@ -1244,7 +1238,9 @@ describe('define attrs', () => { required: true } }, - attrs: Object as AttrsType, + attrs: Object as AttrsType<{ + bar?: number + }>, setup(props, { attrs }) { expectType(props.foo) expectType(attrs.bar) @@ -1254,9 +1250,6 @@ describe('define attrs', () => { }) test('define attrs w/ functional component', () => { - type CompAttrs = { - bar: number - } const MyComp = defineComponent( (props: { foo: string }, ctx) => { expectType(ctx.attrs.bar) @@ -1266,61 +1259,46 @@ describe('define attrs', () => { ) }, { - attrs: Object as AttrsType + attrs: Object as AttrsType<{ + bar?: number + }> } ) expectType() }) test('define attrs as low priority', () => { - type CompAttrs = { - foo: number - } const MyComp = defineComponent({ props: { foo: String }, - attrs: Object as AttrsType, + attrs: Object as AttrsType<{ + foo?: number + }>, created() { // @ts-expect-error this.$attrs.foo + + expectType(this.foo); } }) expectType() }) - test('attrs is always optional w/ object props', () => { - type CompAttrs = { - bar: number - } + test('define required attrs', () => { const MyComp = defineComponent({ - attrs: Object as AttrsType, + attrs: Object as AttrsType<{ + bar: number + }>, created() { expectType(this.$attrs.bar) } }) + expectType() + // @ts-expect-error expectType() }) - test('attrs is always optional w/ functional component', () => { - type CompAttrs = { - bar: number - } - const MyComp = defineComponent( - (props: { foo: string }, ctx) => { - expectType(ctx.attrs.bar) - return () => ( - // return a render function (both JSX and h() works) -
{props.foo}
- ) - }, - { - attrs: Object as AttrsType - } - ) - expectType() - }) - test('define attrs w/ no attrs', () => { const MyComp = defineComponent({ props: { @@ -1333,6 +1311,23 @@ describe('define attrs', () => { // @ts-expect-error expectType() }) + + test('default attrs like class, style', () => { + const MyComp = defineComponent({ + props: { + foo: String + }, + attrs: Object as AttrsType<{ + bar?: number + }>, + created() { + expectType(this.$attrs.bar) + expectType(this.$attrs.class) + expectType(this.$attrs.style) + } + }) + expectType() + }) }) // #5948 diff --git a/packages/dts-test/defineCustomElement.test-d.ts b/packages/dts-test/defineCustomElement.test-d.ts index 53b4facb785..8dfe7dae9cb 100644 --- a/packages/dts-test/defineCustomElement.test-d.ts +++ b/packages/dts-test/defineCustomElement.test-d.ts @@ -65,80 +65,54 @@ describe('inject', () => { describe('define attrs', () => { test('define attrs w/ object props', () => { - type CompAttrs = { - bar: number - baz?: string - } defineCustomElement({ props: { foo: String }, - attrs: Object as AttrsType, + attrs: Object as AttrsType<{ + bar?: number + }>, created() { expectType(this.$attrs.bar) - expectType(this.$attrs.baz) } }) }) test('define attrs w/ array props', () => { - type CompAttrs = { - bar: number - baz?: string - } defineCustomElement({ props: ['foo'], - attrs: Object as AttrsType, + attrs: Object as AttrsType<{ + bar?: number + }>, created() { expectType(this.$attrs.bar) - expectType(this.$attrs.baz) } }) }) test('define attrs w/ no props', () => { - type CompAttrs = { - bar: number - baz?: string - } defineCustomElement({ - attrs: Object as AttrsType, + attrs: Object as AttrsType<{ + bar?: number + }>, created() { expectType(this.$attrs.bar) - expectType(this.$attrs.baz) } }) }) - test('define attrs w/ function component', () => { - type CompAttrs = { - bar: number - baz?: string - } - defineCustomElement( - (_props: { foo: string }, ctx) => { - expectType(ctx.attrs.bar) - expectType(ctx.attrs.bar) - expectType(ctx.attrs.baz) - }, - { - attrs: Object as AttrsType - } - ) - }) - test('define attrs as low priority', () => { - type CompAttrs = { - foo: number - } defineCustomElement({ props: { - foo: String + foo: String, }, - attrs: Object as AttrsType, + attrs: Object as AttrsType<{ + foo: number + }>, created() { // @ts-expect-error this.$attrs.foo + expectType(this.foo) } }) }) @@ -154,4 +128,30 @@ describe('define attrs', () => { } }) }) + + test('default attrs like class, style', () => { + defineCustomElement({ + props: { + foo: String + }, + created() { + expectType(this.$attrs.class) + expectType(this.$attrs.style) + } + }) + }) + + test('define required attrs', () => { + defineCustomElement({ + props: { + foo: String + }, + attrs: Object as AttrsType<{ + bar: number + }>, + created() { + expectType(this.$attrs.bar) + } + }) + }) }) diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index 33106ca8c52..bf1787d8027 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -122,9 +122,9 @@ export function defineComponent< props?: (keyof Props)[] emits?: E | EE[] slots?: S - attrs?: Partial + attrs?: Attrs } -): (props: Props & EmitsToProps & Partial) => any +): (props: Props & EmitsToProps & PropsAttrs) => any export function defineComponent< Props extends Record, E extends EmitsOptions = {}, diff --git a/packages/runtime-core/src/componentPublicInstance.ts b/packages/runtime-core/src/componentPublicInstance.ts index ff14f4270c9..d5264e91550 100644 --- a/packages/runtime-core/src/componentPublicInstance.ts +++ b/packages/runtime-core/src/componentPublicInstance.ts @@ -218,7 +218,7 @@ export type ComponentPublicInstance< Attrs extends AttrsType = {}, PropsAttrs = IsSameType, Data> extends true ? {} - : Partial, keyof (P & PublicProps)>> + : Omit, keyof (P & PublicProps)> > = { $: ComponentInternalInstance $data: D @@ -227,10 +227,8 @@ export type ComponentPublicInstance< ? Partial & Omit

& PropsAttrs : P & PublicProps & PropsAttrs > - $attrs: Partial< - Omit, keyof (P & PublicProps)> & + $attrs: Omit, keyof (P & PublicProps)> & AllowedComponentProps - > $refs: Data $slots: UnwrapSlotsType $root: ComponentPublicInstance | null diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index d94c2f576f3..f8cf6652d31 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -18,8 +18,7 @@ import { ConcreteComponent, ClassComponent, Component, - isClassComponent, - AllowedComponentProps + isClassComponent } from './component' import { RawSlots } from './componentSlots' import { isProxy, Ref, toRaw, ReactiveFlags, isRef } from '@vue/reactivity' @@ -817,7 +816,7 @@ export function normalizeChildren(vnode: VNode, children: unknown) { vnode.shapeFlag |= type } -export function mergeProps(...args: (AllowedComponentProps | Data)[]) { +export function mergeProps(...args: (Data & VNodeProps)[]) { const ret: Data = {} for (let i = 0; i < args.length; i++) { const toMerge = args[i] @@ -830,7 +829,7 @@ export function mergeProps(...args: (AllowedComponentProps | Data)[]) { ret.style = normalizeStyle([ret.style, toMerge.style]) } else if (isOn(key)) { const existing = ret[key] - const incoming = (toMerge as Data)[key] + const incoming = toMerge[key] if ( incoming && existing !== incoming && @@ -841,7 +840,7 @@ export function mergeProps(...args: (AllowedComponentProps | Data)[]) { : incoming } } else if (key !== '') { - ret[key] = (toMerge as Data)[key] + ret[key] = toMerge[key] } } } diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index 1a47eaf9a01..2e23e213102 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -38,15 +38,11 @@ export type VueElementConstructor

= { export function defineCustomElement< Props, RawBindings = object, - Attrs extends AttrsType = {} >( setup: ( props: Readonly, - ctx: SetupContext<{}, {}, Attrs> + ctx: SetupContext ) => RawBindings | RenderFunction, - options?: { - attrs?: Attrs - } ): VueElementConstructor // overload 2: object format with no props @@ -65,7 +61,7 @@ export function defineCustomElement< S extends SlotsType = {}, Attrs extends AttrsType = {} >( - comp: ComponentOptionsWithoutProps< + options: ComponentOptionsWithoutProps< Props, RawBindings, D, @@ -98,7 +94,7 @@ export function defineCustomElement< S extends SlotsType = {}, Attrs extends AttrsType = {} >( - comp: ComponentOptionsWithArrayProps< + options: ComponentOptionsWithArrayProps< PropNames, RawBindings, D, @@ -131,7 +127,7 @@ export function defineCustomElement< S extends SlotsType = {}, Attrs extends AttrsType = {} >( - comp: ComponentOptionsWithObjectProps< + options: ComponentOptionsWithObjectProps< PropsOptions, RawBindings, D, @@ -157,7 +153,7 @@ export function defineCustomElement(options: { /*! #__NO_SIDE_EFFECTS__ */ export function defineCustomElement( options: any, - hydrate?: any + hydrate?: RootHydrateFunction ): VueElementConstructor { const Comp = defineComponent(options) as any class VueCustomElement extends VueElement {