feat(utils): add buildProps (#3718)

This commit is contained in:
三咲智子
2021-09-29 09:52:58 +08:00
committed by GitHub
parent 5cc4479060
commit bf8697cc0e
2 changed files with 92 additions and 43 deletions

View File

@@ -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<T> = Readonly<T> extends Readonly<
? ResolvePropType<A[]>
: ResolvePropType<T>
type BuildPropOption<T, D, R, V, C> = {
type?: T
values?: readonly V[]
required?: R
default?: R extends true
? never
: D extends Record<string, unknown> | Array<any>
? () => D
: D
validator?: ((val: any) => val is C) | ((val: any) => boolean)
}
type IfUnknown<T, V> = [unknown] extends T ? V : T
type BuildPropType<T, V, C> =
| (T extends PropWrapper<unknown>
? T[typeof wrapperKey]
: [V] extends [never]
? ResolvePropTypeWithReadonly<T>
: never)
| V
| C
type BuildPropDefault<D, R> = R extends true
? { readonly default?: undefined }
: {
readonly default: Exclude<D, undefined> extends never
? undefined
: Exclude<
D extends Record<string, unknown> | Array<any> ? () => D : D,
undefined
>
}
type BuildPropReturn<T, D, R, V, C> = {
readonly type: PropType<
BuildPropType<IfUnknown<T, never>, IfUnknown<V, never>, IfUnknown<C, never>>
>
readonly required: IfUnknown<R, false>
readonly validator: ((val: unknown) => boolean) | undefined
} & BuildPropDefault<IfUnknown<D, never>, IfUnknown<R, false>>
/**
* @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<string, unknown> | Array<any>
? () => D
: D
validator?: ((val: any) => val is C) | ((val: any) => boolean)
} = {}) {
>(option: BuildPropOption<T, D, R, V, C> = {}) {
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<D, undefined> extends never ? false : true
type Type = PropType<
| (T extends PropWrapper<unknown>
? T[typeof wrapperKey]
: [V] extends [never]
? ResolvePropTypeWithReadonly<T>
: 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<string, unknown> | Array<any> ? () => D : D,
undefined
>
: undefined
})
} as unknown as BuildPropReturn<T, D, R, V, C>
}
export const buildProps = <
O extends Record<string, BuildPropOption<any, any, any, any, any>>
>(
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<T, D, R, V, C>
: never
}
export const definePropType = <T>(val: any) =>
({ [wrapperKey]: val } as PropWrapper<T>)

View File

@@ -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
}
}>()
})
})