From bf8697cc0e76e2ae20b3573a6e115fa76491445f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90?= Date: Wed, 29 Sep 2021 09:52:58 +0800 Subject: [PATCH] feat(utils): add buildProps (#3718) --- packages/utils/props.ts | 105 ++++++++++++++++++------------ packages/utils/tests/prop.spec.ts | 30 ++++++++- 2 files changed, 92 insertions(+), 43 deletions(-) diff --git a/packages/utils/props.ts b/packages/utils/props.ts index 0d8c99bc13..6355170192 100644 --- a/packages/utils/props.ts +++ b/packages/utils/props.ts @@ -1,3 +1,4 @@ +import mapValues from 'lodash/mapValues' import { debugWarn } from './error' import type { ExtractPropTypes, PropType } from '@vue/runtime-core' import type { Mutable } from './types' @@ -17,6 +18,47 @@ type ResolvePropTypeWithReadonly = Readonly extends Readonly< ? ResolvePropType : ResolvePropType +type BuildPropOption = { + type?: T + values?: readonly V[] + required?: R + default?: R extends true + ? never + : D extends Record | Array + ? () => D + : D + validator?: ((val: any) => val is C) | ((val: any) => boolean) +} + +type IfUnknown = [unknown] extends T ? V : T + +type BuildPropType = + | (T extends PropWrapper + ? T[typeof wrapperKey] + : [V] extends [never] + ? ResolvePropTypeWithReadonly + : never) + | V + | C + +type BuildPropDefault = R extends true + ? { readonly default?: undefined } + : { + readonly default: Exclude extends never + ? undefined + : Exclude< + D extends Record | Array ? () => D : D, + undefined + > + } +type BuildPropReturn = { + readonly type: PropType< + BuildPropType, IfUnknown, IfUnknown> + > + readonly required: IfUnknown + readonly validator: ((val: unknown) => boolean) | undefined +} & BuildPropDefault, IfUnknown> + /** * @description Build prop. It can better optimize prop types * @description 生成 prop,能更好地优化类型 @@ -47,23 +89,9 @@ export function buildProp< R extends boolean = false, V = never, C = never ->({ - values, - required, - default: defaultValue, - type, - validator, -}: { - type?: T - values?: readonly V[] - required?: R - default?: R extends true - ? never - : D extends Record | Array - ? () => D - : D - validator?: ((val: any) => val is C) | ((val: any) => boolean) -} = {}) { +>(option: BuildPropOption = {}) { + const { values, required, default: defaultValue, type, validator } = option + const _validator = values || validator ? (val: unknown) => { @@ -88,38 +116,31 @@ export function buildProp< } : undefined - type HasDefaultValue = Exclude extends never ? false : true - type Type = PropType< - | (T extends PropWrapper - ? T[typeof wrapperKey] - : [V] extends [never] - ? ResolvePropTypeWithReadonly - : never) - | V - | C - > - return { 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 - }) + } as unknown as BuildPropReturn } +export const buildProps = < + O extends Record> +>( + options: O +) => + mapValues(options, (option) => buildProp(option)) as unknown as { + [K in keyof O]: O[K] extends BuildPropOption< + infer T, + infer D, + infer R, + infer V, + infer C + > + ? BuildPropReturn + : never + } + export const definePropType = (val: any) => ({ [wrapperKey]: val } as PropWrapper) diff --git a/packages/utils/tests/prop.spec.ts b/packages/utils/tests/prop.spec.ts index 0ac114864e..fac13b4ecb 100644 --- a/packages/utils/tests/prop.spec.ts +++ b/packages/utils/tests/prop.spec.ts @@ -1,5 +1,5 @@ import { expectTypeOf } from 'expect-type' -import { buildProp, definePropType, mutable, keyOf } from '../props' +import { buildProp, definePropType, mutable, keyOf, buildProps } from '../props' import type { PropType, ExtractPropTypes } from 'vue' @@ -263,3 +263,31 @@ describe('buildProp', () => { }>() }) }) + +describe('buildProps', () => { + it('test buildProps', () => { + expectTypeOf( + buildProps({ + key1: { + type: definePropType<'a' | 'b'>(String), + }, + key2: { + values: [1, 2, 3, 4], + }, + } as const) + ).toEqualTypeOf<{ + readonly key1: { + readonly type: PropType<'a' | 'b'> + readonly required: false + readonly default: undefined + readonly validator: ((val: unknown) => boolean) | undefined + } + readonly key2: { + readonly type: PropType<1 | 2 | 3 | 4> + readonly required: false + readonly default: undefined + readonly validator: ((val: unknown) => boolean) | undefined + } + }>() + }) +})