123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- 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;
|