123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- import { warn } from 'vue'
- import { fromPairs } from 'lodash-unified'
- import { isObject } from '../../types'
- import { hasOwn } from '../../objects'
- import type { PropType } from 'vue'
- import type {
- EpProp,
- EpPropConvert,
- EpPropFinalized,
- EpPropInput,
- EpPropMergeType,
- IfEpProp,
- IfNativePropType,
- NativePropType,
- } from './types'
- export const epPropKey = '__epPropKey'
- export const definePropType = <T>(val: any): PropType<T> => val
- export const isEpProp = (val: unknown): val is EpProp<any, any, any> =>
- isObject(val) && !!(val as any)[epPropKey]
- /**
- * @description Build prop. It can better optimize prop types
- * @description 生成 prop,能更好地优化类型
- * @example
- // limited options
- // the type will be PropType<'light' | 'dark'>
- buildProp({
- type: String,
- values: ['light', 'dark'],
- } as const)
- * @example
- // limited options and other types
- // the type will be PropType<'small' | 'large' | number>
- buildProp({
- type: [String, Number],
- values: ['small', 'large'],
- validator: (val: unknown): val is number => typeof val === 'number',
- } as const)
- @link see more: https://github.com/element-plus/element-plus/pull/3341
- */
- export const buildProp = <
- Type = never,
- Value = never,
- Validator = never,
- Default extends EpPropMergeType<Type, Value, Validator> = never,
- Required extends boolean = false
- >(
- prop: EpPropInput<Type, Value, Validator, Default, Required>,
- key?: string
- ): EpPropFinalized<Type, Value, Validator, Default, Required> => {
- // filter native prop type and nested prop, e.g `null`, `undefined` (from `buildProps`)
- if (!isObject(prop) || isEpProp(prop)) return prop as any
- const { values, required, default: defaultValue, type, validator } = prop
- const _validator =
- values || validator
- ? (val: unknown) => {
- let valid = false
- let allowedValues: unknown[] = []
- if (values) {
- allowedValues = Array.from(values)
- if (hasOwn(prop, 'default')) {
- allowedValues.push(defaultValue)
- }
- valid ||= allowedValues.includes(val)
- }
- if (validator) valid ||= validator(val)
- if (!valid && allowedValues.length > 0) {
- const allowValuesText = [...new Set(allowedValues)]
- .map((value) => JSON.stringify(value))
- .join(', ')
- warn(
- `Invalid prop: validation failed${
- key ? ` for prop "${key}"` : ''
- }. Expected one of [${allowValuesText}], got value ${JSON.stringify(
- val
- )}.`
- )
- }
- return valid
- }
- : undefined
- const epProp: any = {
- type,
- required: !!required,
- validator: _validator,
- [epPropKey]: true,
- }
- if (hasOwn(prop, 'default')) epProp.default = defaultValue
- return epProp
- }
- export const buildProps = <
- Props extends Record<
- string,
- | { [epPropKey]: true }
- | NativePropType
- | EpPropInput<any, any, any, any, any>
- >
- >(
- props: Props
- ): {
- [K in keyof Props]: IfEpProp<
- Props[K],
- Props[K],
- IfNativePropType<Props[K], Props[K], EpPropConvert<Props[K]>>
- >
- } =>
- fromPairs(
- Object.entries(props).map(([key, option]) => [
- key,
- buildProp(option as any, key),
- ])
- ) as any
|