From 051c5669665bf44d4b9fa527dc5309b76e17b836 Mon Sep 17 00:00:00 2001 From: pikax <david-181@hotmail.com> Date: Tue, 16 May 2023 13:52:26 +0100 Subject: [PATCH] types(withDefaults): Allow to be used with generic defineProps --- packages/dts-test/setupHelpers.test-d.ts | 34 +++++++++++++ packages/runtime-core/src/apiSetupHelpers.ts | 52 +++++++++++--------- 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/packages/dts-test/setupHelpers.test-d.ts b/packages/dts-test/setupHelpers.test-d.ts index 9b68b345268..77342590dc6 100644 --- a/packages/dts-test/setupHelpers.test-d.ts +++ b/packages/dts-test/setupHelpers.test-d.ts @@ -100,6 +100,40 @@ describe('defineProps w/ union type declaration + withDefaults', () => { ) }) +describe('defineProps w/ generic type declaration + withDefaults', <T extends number, TA extends { + a: string +}, TString extends string>() => { + const res = withDefaults( + defineProps<{ + n?: number + bool?: boolean + + generic1?: T[] | { x: T } + generic2?: { x: T } + generic3?: TString + generic4?: TA + }>(), + { + n: 123, + + generic1: () => [123, 33] as T[], + generic2: () => ({ x: 123 } as { x: T }), + + generic3: () => 'test' as TString, + generic4: () => ({ a: 'test' } as TA) + } + ) + + res.n + 1 + + expectType<T[] | { x: T }>(res.generic1) + expectType<{ x: T }>(res.generic2) + expectType<TString>(res.generic3) + expectType<TA>(res.generic4) + + expectType<boolean>(res.bool) +}) + describe('defineProps w/ runtime declaration', () => { // runtime declaration const props = defineProps({ diff --git a/packages/runtime-core/src/apiSetupHelpers.ts b/packages/runtime-core/src/apiSetupHelpers.ts index 3ad330a1c27..76ad08cf63a 100644 --- a/packages/runtime-core/src/apiSetupHelpers.ts +++ b/packages/runtime-core/src/apiSetupHelpers.ts @@ -81,7 +81,10 @@ export function defineProps< PP extends ComponentObjectPropsOptions = ComponentObjectPropsOptions >(props: PP): Prettify<Readonly<ExtractPropTypes<PP>>> // overload 3: typed-based declaration -export function defineProps<TypeProps>(): DefineProps<TypeProps> +export function defineProps<TypeProps>(): DefineProps< + TypeProps, + BooleanKey<TypeProps> +> // implementation export function defineProps() { if (__DEV__) { @@ -90,8 +93,8 @@ export function defineProps() { return null as any } -type DefineProps<T> = Readonly<T> & { - readonly [K in BooleanKey<T>]-?: boolean +type DefineProps<T, BKeys extends keyof T> = Readonly<T> & { + readonly [K in BKeys]-?: boolean } type BooleanKey<T, K extends keyof T = keyof T> = K extends any @@ -281,26 +284,27 @@ interface DefineModelOptions { type NotUndefined<T> = T extends undefined ? never : T type InferDefaults<T> = { - [K in keyof T]?: InferDefault<T, NotUndefined<T[K]>> + [K in keyof T]?: InferDefault<T, T[K]> } -type InferDefault<P, T> = T extends - | null - | number - | string - | boolean - | symbol - | Function - ? T | ((props: P) => T) - : (props: P) => T - -type PropsWithDefaults<Base, Defaults> = Base & { - [K in keyof Defaults]: K extends keyof Base +type NativeType = null | number | string | boolean | symbol | Function + +type InferDefault<P, T> = + | ((props: P) => T & {}) + | (T extends NativeType ? T : never) + +type PropsWithDefaults< + T, + Defaults extends InferDefaults<T>, + BKeys extends keyof T +> = Omit<T, keyof Defaults> & { + [K in keyof Defaults]-?: K extends keyof T ? Defaults[K] extends undefined - ? Base[K] - : NotUndefined<Base[K]> + ? T[K] + : NotUndefined<T[K]> : never -} +} & { readonly [K in BKeys]-?: boolean } + /** * Vue `<script setup>` compiler macro for providing props default values when * using type-based `defineProps` declaration. @@ -321,10 +325,14 @@ type PropsWithDefaults<Base, Defaults> = Base & { * * @see {@link https://vuejs.org/guide/typescript/composition-api.html#typing-component-props} */ -export function withDefaults<Props, Defaults extends InferDefaults<Props>>( - props: Props, +export function withDefaults< + T, + BKeys extends keyof T, + Defaults extends InferDefaults<T> +>( + props: DefineProps<T, BKeys>, defaults: Defaults -): PropsWithDefaults<Props, Defaults> { +): PropsWithDefaults<T, Defaults, BKeys> { if (__DEV__) { warnRuntimeUsage(`withDefaults`) }