diff --git a/packages/utils/props.ts b/packages/utils/props.ts index bc6569c382..0d8c99bc13 100644 --- a/packages/utils/props.ts +++ b/packages/utils/props.ts @@ -64,6 +64,30 @@ export function buildProp< : D validator?: ((val: any) => val is C) | ((val: any) => boolean) } = {}) { + const _validator = + values || validator + ? (val: unknown) => { + let valid = false + let allowedValues: unknown[] = [] + + if (values) { + allowedValues = [...values, defaultValue] + valid ||= allowedValues.includes(val) + } + if (validator) valid ||= validator(val) + + if (!valid && allowedValues.length > 0) { + debugWarn( + `Vue warn`, + `Invalid prop: Expected one of (${allowedValues.join( + ', ' + )}), got value ${val}` + ) + } + return valid + } + : undefined + type HasDefaultValue = Exclude extends never ? false : true type Type = PropType< | (T extends PropWrapper @@ -76,42 +100,24 @@ export function buildProp< > return { - type: ((type as any)?.[wrapperKey] || type) as unknown as Type, - required: !!required as R, - - default: defaultValue as unknown as R extends true - ? never - : HasDefaultValue extends true - ? Exclude< - D extends Record | Array ? () => D : D, - undefined - > - : undefined, - - validator: - values || validator - ? (val: unknown) => { - let valid = false - let allowedValues: unknown[] = [] - - if (values) { - allowedValues = [...values, defaultValue] - valid ||= allowedValues.includes(val) - } - if (validator) valid ||= validator(val) - - if (!valid && allowedValues.length > 0) { - debugWarn( - `Vue warn`, - `Invalid prop: Expected one of (${allowedValues.join( - ', ' - )}), got value ${val}` - ) - } - return valid - } - : undefined, - } as const + type: (type as any)?.[wrapperKey] || type, + required: !!required, + default: defaultValue, + validator: _validator, + } as unknown as { + readonly type: Type + readonly required: R + readonly validator: typeof _validator + } & (R extends true + ? { readonly default?: undefined } + : { + readonly default: HasDefaultValue extends true + ? Exclude< + D extends Record | Array ? () => D : D, + undefined + > + : undefined + }) } export const definePropType = (val: any) => diff --git a/packages/utils/tests/prop.spec.ts b/packages/utils/tests/prop.spec.ts index b973624712..0ac114864e 100644 --- a/packages/utils/tests/prop.spec.ts +++ b/packages/utils/tests/prop.spec.ts @@ -1,7 +1,7 @@ import { expectTypeOf } from 'expect-type' import { buildProp, definePropType, mutable, keyOf } from '../props' -import type { PropType } from 'vue' +import type { PropType, ExtractPropTypes } from 'vue' describe('buildProp', () => { it('Only type', () => { @@ -67,7 +67,7 @@ describe('buildProp', () => { ).toEqualTypeOf<{ readonly type: PropType<'a' | 'b' | 'c'> readonly required: true - readonly default: never + readonly default?: undefined readonly validator: ((val: unknown) => boolean) | undefined }>() }) @@ -147,7 +147,7 @@ describe('buildProp', () => { ).toEqualTypeOf<{ readonly type: PropType readonly required: true - readonly default: never + readonly default?: undefined readonly validator: ((val: unknown) => boolean) | undefined }>() }) @@ -197,7 +197,7 @@ describe('buildProp', () => { ).toEqualTypeOf<{ readonly type: PropType readonly required: true - readonly default: never + readonly default?: undefined readonly validator: ((val: unknown) => boolean) | undefined }>() }) @@ -243,4 +243,23 @@ describe('buildProp', () => { readonly validator: ((val: unknown) => boolean) | undefined }>() }) + + it('extract', () => { + const props = { + key1: buildProp({ + type: String, + required: true, + }), + key2: buildProp({ + type: [String, Number], + required: true, + }), + } as const + type Extracted = ExtractPropTypes + + expectTypeOf().toEqualTypeOf<{ + readonly key1: string + readonly key2: string | number + }>() + }) })