element-plus/packages/utils/props.ts
2021-09-23 20:06:07 +08:00

132 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { debugWarn } from './error'
import type { ExtractPropTypes, PropType } from '@vue/runtime-core'
import type { Mutable } from './types'
const wrapperKey = Symbol()
export type PropWrapper<T> = { [wrapperKey]: T }
type ResolveProp<T> = ExtractPropTypes<{
key: { type: T; required: true }
}>['key']
type ResolvePropType<T> = ResolveProp<T> extends { type: infer V }
? V
: ResolveProp<T>
type ResolvePropTypeWithReadonly<T> = Readonly<T> extends Readonly<
Array<infer A>
>
? ResolvePropType<A[]>
: ResolvePropType<T>
/**
* @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' | 'medium' | number>
buildProp({
type: [String, Number],
values: ['small', 'medium'],
validator: (val: unknown): val is number => typeof val === 'number',
} as const)
@link see more: https://github.com/element-plus/element-plus/pull/3341
*/
export function buildProp<
T = never,
D extends
| (T extends PropWrapper<any>
? T[typeof wrapperKey]
: ResolvePropTypeWithReadonly<T>)
| V = never,
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)
} = {}) {
const _validator =
values || validator
? (val: unknown) => {
let valid = false
let allowedValues: unknown[] = []
if (values) {
allowedValues = [...values, defaultValue]
valid ||= allowedValues.includes(val)
}
if (validator) valid ||= validator(val)
if (!valid && allowedValues.length > 0) {
debugWarn(
`Vue warn`,
`Invalid prop: Expected one of (${allowedValues.join(
', '
)}), got value ${val}`
)
}
return valid
}
: 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
})
}
export const definePropType = <T>(val: any) =>
({ [wrapperKey]: val } as PropWrapper<T>)
export const keyOf = <T>(arr: T) => Object.keys(arr) as Array<keyof T>
export const mutable = <T extends readonly any[] | Record<string, unknown>>(
val: T
) => val as Mutable<typeof val>
export const componentSize = ['large', 'medium', 'small', 'mini'] as const