diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index 0fd25bc7..7c55033c 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -65,7 +65,7 @@ import { import { warn } from './warning' import { VNodeChild } from './vnode' import { callWithAsyncErrorHandling } from './errorHandling' -import { UnionToIntersection } from './helpers/typeUtils' +import { LooseRequired, UnionToIntersection } from './helpers/typeUtils' import { deepMergeData } from './compat/data' import { DeprecationTypes } from './compat/compatConfig' import { @@ -130,9 +130,13 @@ export interface ComponentOptionsBase< ComponentCustomOptions { setup?: ( this: void, - props: Props & - UnionToIntersection> & - UnionToIntersection>, + props: Readonly< + LooseRequired< + Props & + UnionToIntersection> & + UnionToIntersection> + > + >, ctx: SetupContext ) => Promise | RawBindings | RenderFunction | void name?: string diff --git a/packages/runtime-core/src/helpers/typeUtils.ts b/packages/runtime-core/src/helpers/typeUtils.ts index 64bf5129..222e7271 100644 --- a/packages/runtime-core/src/helpers/typeUtils.ts +++ b/packages/runtime-core/src/helpers/typeUtils.ts @@ -3,3 +3,6 @@ export type UnionToIntersection = (U extends any : never) extends ((k: infer I) => void) ? I : never + +// make keys required but keep undefined values +export type LooseRequired = { [P in string & keyof T]: T[P] } diff --git a/test-dts/component.test-d.ts b/test-dts/component.test-d.ts index 06368e37..e679ffa6 100644 --- a/test-dts/component.test-d.ts +++ b/test-dts/component.test-d.ts @@ -9,7 +9,8 @@ import { expectType, ShallowUnwrapRef, FunctionalComponent, - ComponentPublicInstance + ComponentPublicInstance, + toRefs } from './index' declare function extractComponentOptions( @@ -42,6 +43,27 @@ describe('object props', () => { object?: object } + interface ExpectedRefs { + a: Ref + b: Ref + e: Ref + bb: Ref + bbb: Ref + cc: Ref + dd: Ref<{ n: 1 }> + ee: Ref<(() => string) | undefined> + ff: Ref<((a: number, b: string) => { a: boolean }) | undefined> + ccc: Ref + ddd: Ref + eee: Ref<() => { a: string }> + fff: Ref<(a: number, b: string) => { a: boolean }> + hhh: Ref + ggg: Ref<'foo' | 'bar'> + ffff: Ref<(a: number, b: string) => { a: boolean }> + validated: Ref + object: Ref + } + describe('defineComponent', () => { const MyComponent = defineComponent({ props: { @@ -111,6 +133,26 @@ describe('object props', () => { object: Object as PropType }, setup(props) { + const refs = toRefs(props) + expectType(refs.a) + expectType(refs.b) + expectType(refs.e) + expectType(refs.bb) + expectType(refs.bbb) + expectType(refs.cc) + expectType(refs.dd) + expectType(refs.ee) + expectType(refs.ff) + expectType(refs.ccc) + expectType(refs.ddd) + expectType(refs.eee) + expectType(refs.fff) + expectType(refs.hhh) + expectType(refs.ggg) + expectType(refs.ffff) + expectType(refs.validated) + expectType(refs.object) + return { setupA: 1, setupB: ref(1),