diff --git a/packages/utils/props.ts b/packages/utils/props.ts index f2a2b59f9c..e949523c1b 100644 --- a/packages/utils/props.ts +++ b/packages/utils/props.ts @@ -20,7 +20,8 @@ type ResolvePropTypeWithReadonly = Readonly extends Readonly< > ? ResolvePropType : ResolvePropType -type IfUnknown = [unknown] extends T ? V : T + +type IfUnknown = [unknown] extends [T] ? V : T export type BuildPropOption, R, V, C> = { type?: T @@ -30,7 +31,7 @@ export type BuildPropOption, R, V, C> = { ? never : D extends Record | Array ? () => D - : D + : (() => D) | D validator?: ((val: any) => val is C) | ((val: any) => boolean) } @@ -48,22 +49,32 @@ export type BuildPropType = _BuildPropType< IfUnknown > -export type BuildPropDefault = R extends true +type _BuildPropDefault = [T] extends [ + // eslint-disable-next-line @typescript-eslint/ban-types + Record | Array | Function +] + ? D + : D extends () => T + ? ReturnType + : D + +export type BuildPropDefault = R extends true ? { readonly default?: undefined } : { readonly default: Exclude extends never ? undefined - : Exclude< - D extends Record | Array ? () => D : D, - undefined - > + : Exclude<_BuildPropDefault, undefined> } export type BuildPropReturn = { readonly type: PropType> readonly required: IfUnknown readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true -} & BuildPropDefault, IfUnknown> +} & BuildPropDefault< + BuildPropType, + IfUnknown, + IfUnknown +> /** * @description Build prop. It can better optimize prop types diff --git a/packages/utils/tests/prop.spec.ts b/packages/utils/tests/prop.spec.ts index 171e705480..f16a7d5924 100644 --- a/packages/utils/tests/prop.spec.ts +++ b/packages/utils/tests/prop.spec.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/ban-types */ + import { expectTypeOf } from 'expect-type' import { buildProp, definePropType, mutable, keyOf, buildProps } from '../props' import type { propKey } from '../props' @@ -103,7 +105,7 @@ describe('buildProp', () => { ).toEqualTypeOf<{ readonly type: PropType readonly required: false - readonly default: () => ['a', 'b'] + readonly default: ['a', 'b'] readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() @@ -122,7 +124,7 @@ describe('buildProp', () => { ).toEqualTypeOf<{ readonly type: PropType readonly required: false - readonly default: () => { key: 'value' } + readonly default: { key: 'value' } readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() @@ -141,7 +143,7 @@ describe('buildProp', () => { ).toEqualTypeOf<{ readonly type: PropType readonly required: false - readonly default: () => { key: string } + readonly default: { key: string } readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() @@ -241,7 +243,7 @@ describe('buildProp', () => { ).toEqualTypeOf<{ readonly type: PropType<{ key: 'a' | 'b' | 'c' } | undefined> readonly required: false - readonly default: () => { key: 'a' } + readonly default: { key: 'a' } readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() @@ -262,6 +264,21 @@ describe('buildProp', () => { }>() }) + it('default value is empty object', () => { + expectTypeOf( + buildProp({ + type: Object, + default: () => mutable({} as const), + } as const) + ).toEqualTypeOf<{ + readonly type: PropType> + readonly required: false + readonly default: {} + readonly validator: ((val: unknown) => boolean) | undefined + [propKey]: true + }>() + }) + it('extract', () => { const props = { key1: buildProp({ @@ -318,6 +335,24 @@ describe('buildProps', () => { key12: buildProp({ type: String, } as const), + + // default generator + key13: { + type: [String, Number, Function], + default: () => '123' as const, + } as const, + key14: { + type: Function, + default: () => '123' as const, + } as const, + key15: { + type: Function, + default: () => () => '123' as const, + } as const, + key16: { + type: String, + default: () => '123' as const, + } as const, } as const) expectTypeOf(props).toEqualTypeOf<{ @@ -371,6 +406,34 @@ describe('buildProps', () => { readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true } + readonly key13: { + readonly type: PropType + readonly required: false + readonly default: '123' + readonly validator: ((val: unknown) => boolean) | undefined + [propKey]: true + } + readonly key14: { + readonly type: PropType + readonly required: false + readonly default: () => '123' + readonly validator: ((val: unknown) => boolean) | undefined + [propKey]: true + } + readonly key15: { + readonly type: PropType + readonly required: false + readonly default: () => () => '123' + readonly validator: ((val: unknown) => boolean) | undefined + [propKey]: true + } + readonly key16: { + readonly type: PropType + readonly required: false + readonly default: '123' + readonly validator: ((val: unknown) => boolean) | undefined + [propKey]: true + } }>() }) })