mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
feat(utils): add buildProps (#3718)
This commit is contained in:
@@ -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>)
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}>()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user