2019-11-07 01:51:06 +08:00
|
|
|
import { toRaw, lock, unlock } from '@vue/reactivity'
|
2019-05-28 18:06:00 +08:00
|
|
|
import {
|
|
|
|
EMPTY_OBJ,
|
|
|
|
camelize,
|
|
|
|
hyphenate,
|
|
|
|
capitalize,
|
|
|
|
isString,
|
|
|
|
isFunction,
|
|
|
|
isArray,
|
2019-06-03 13:44:45 +08:00
|
|
|
isObject,
|
2019-09-24 03:36:30 +08:00
|
|
|
hasOwn,
|
2019-10-25 23:15:04 +08:00
|
|
|
toRawType,
|
2019-10-23 22:34:58 +08:00
|
|
|
PatchFlags,
|
|
|
|
makeMap
|
2019-05-28 18:06:00 +08:00
|
|
|
} from '@vue/shared'
|
|
|
|
import { warn } from './warning'
|
2019-09-07 00:58:31 +08:00
|
|
|
import { Data, ComponentInternalInstance } from './component'
|
2019-05-28 18:06:00 +08:00
|
|
|
|
2019-10-08 21:26:09 +08:00
|
|
|
export type ComponentPropsOptions<P = Data> =
|
|
|
|
| ComponentObjectPropsOptions<P>
|
|
|
|
| string[]
|
|
|
|
|
|
|
|
export type ComponentObjectPropsOptions<P = Data> = {
|
2019-06-01 00:47:05 +08:00
|
|
|
[K in keyof P]: Prop<P[K]> | null
|
2019-05-28 18:06:00 +08:00
|
|
|
}
|
|
|
|
|
2019-09-07 00:58:31 +08:00
|
|
|
export type Prop<T> = PropOptions<T> | PropType<T>
|
2019-05-28 18:06:00 +08:00
|
|
|
|
2019-10-30 23:11:23 +08:00
|
|
|
type DefaultFactory<T> = () => T | null | undefined
|
|
|
|
|
2019-05-31 18:07:43 +08:00
|
|
|
interface PropOptions<T = any> {
|
2019-05-28 18:06:00 +08:00
|
|
|
type?: PropType<T> | true | null
|
|
|
|
required?: boolean
|
2019-10-30 23:11:23 +08:00
|
|
|
default?: T | DefaultFactory<T> | null | undefined
|
2019-10-22 23:26:48 +08:00
|
|
|
validator?(value: unknown): boolean
|
2019-05-31 18:07:43 +08:00
|
|
|
}
|
|
|
|
|
2019-06-01 00:47:05 +08:00
|
|
|
export type PropType<T> = PropConstructor<T> | PropConstructor<T>[]
|
|
|
|
|
2019-10-22 23:26:48 +08:00
|
|
|
type PropConstructor<T = any> = { new (...args: any[]): T & object } | { (): T }
|
2019-06-01 00:47:05 +08:00
|
|
|
|
2019-10-05 22:48:54 +08:00
|
|
|
type RequiredKeys<T, MakeDefaultRequired> = {
|
2019-06-12 15:43:19 +08:00
|
|
|
[K in keyof T]: T[K] extends
|
|
|
|
| { required: true }
|
2019-10-05 22:48:54 +08:00
|
|
|
| (MakeDefaultRequired extends true ? { default: any } : never)
|
2019-06-12 15:43:19 +08:00
|
|
|
? K
|
|
|
|
: never
|
2019-06-01 00:47:05 +08:00
|
|
|
}[keyof T]
|
|
|
|
|
2019-10-05 22:48:54 +08:00
|
|
|
type OptionalKeys<T, MakeDefaultRequired> = Exclude<
|
2019-06-12 15:43:19 +08:00
|
|
|
keyof T,
|
2019-10-05 22:48:54 +08:00
|
|
|
RequiredKeys<T, MakeDefaultRequired>
|
2019-06-12 15:43:19 +08:00
|
|
|
>
|
2019-06-01 00:47:05 +08:00
|
|
|
|
|
|
|
type InferPropType<T> = T extends null
|
2019-06-01 17:43:41 +08:00
|
|
|
? any // null & true would fail to infer
|
|
|
|
: T extends { type: null | true }
|
2019-10-22 23:52:29 +08:00
|
|
|
? any // somehow `ObjectConstructor` when inferred from { (): T } becomes `any`
|
|
|
|
: T extends ObjectConstructor | { type: ObjectConstructor }
|
|
|
|
? { [key: string]: any }
|
|
|
|
: T extends Prop<infer V> ? V : T
|
2019-06-01 00:47:05 +08:00
|
|
|
|
2019-06-12 15:43:19 +08:00
|
|
|
export type ExtractPropTypes<
|
|
|
|
O,
|
2019-10-05 22:48:54 +08:00
|
|
|
MakeDefaultRequired extends boolean = true
|
2019-06-12 15:43:19 +08:00
|
|
|
> = O extends object
|
2019-11-10 07:40:25 +08:00
|
|
|
? { [K in RequiredKeys<O, MakeDefaultRequired>]: InferPropType<O[K]> } &
|
|
|
|
{ [K in OptionalKeys<O, MakeDefaultRequired>]?: InferPropType<O[K]> }
|
2019-06-01 00:47:05 +08:00
|
|
|
: { [K in string]: any }
|
2019-05-28 18:06:00 +08:00
|
|
|
|
|
|
|
const enum BooleanFlags {
|
2019-11-25 05:00:46 +08:00
|
|
|
shouldCast,
|
|
|
|
shouldCastTrue
|
2019-05-28 18:06:00 +08:00
|
|
|
}
|
|
|
|
|
2019-06-01 00:47:05 +08:00
|
|
|
type NormalizedProp =
|
|
|
|
| null
|
|
|
|
| (PropOptions & {
|
|
|
|
[BooleanFlags.shouldCast]?: boolean
|
|
|
|
[BooleanFlags.shouldCastTrue]?: boolean
|
|
|
|
})
|
2019-05-28 18:06:00 +08:00
|
|
|
|
2019-12-13 11:07:48 +08:00
|
|
|
// normalized value is a tuple of the actual normalized options
|
|
|
|
// and an array of prop keys that need value casting (booleans and defaults)
|
|
|
|
type NormalizedPropsOptions = [Record<string, NormalizedProp>, string[]]
|
2019-05-28 18:06:00 +08:00
|
|
|
|
|
|
|
// resolve raw VNode data.
|
|
|
|
// - filter out reserved keys (key, ref, slots)
|
|
|
|
// - extract class and style into $attrs (to be merged onto child
|
|
|
|
// component root)
|
|
|
|
// - for the rest:
|
|
|
|
// - if has declared props: put declared ones in `props`, the rest in `attrs`
|
|
|
|
// - else: everything goes in `props`.
|
|
|
|
|
|
|
|
export function resolveProps(
|
2019-09-07 00:58:31 +08:00
|
|
|
instance: ComponentInternalInstance,
|
2019-10-22 23:26:48 +08:00
|
|
|
rawProps: Data | null,
|
2019-05-28 18:06:00 +08:00
|
|
|
_options: ComponentPropsOptions | void
|
2019-05-29 11:36:16 +08:00
|
|
|
) {
|
2019-05-28 18:06:00 +08:00
|
|
|
const hasDeclaredProps = _options != null
|
2019-05-29 09:18:45 +08:00
|
|
|
if (!rawProps && !hasDeclaredProps) {
|
2019-05-29 11:36:16 +08:00
|
|
|
return
|
2019-05-28 18:06:00 +08:00
|
|
|
}
|
2019-05-30 23:16:15 +08:00
|
|
|
|
2019-12-13 11:07:48 +08:00
|
|
|
const { 0: options, 1: needCastKeys } = normalizePropsOptions(_options)!
|
2019-10-22 23:26:48 +08:00
|
|
|
const props: Data = {}
|
|
|
|
let attrs: Data | undefined = void 0
|
2019-05-30 23:16:15 +08:00
|
|
|
|
|
|
|
// update the instance propsProxy (passed to setup()) to trigger potential
|
|
|
|
// changes
|
|
|
|
const propsProxy = instance.propsProxy
|
|
|
|
const setProp = propsProxy
|
2019-10-22 23:26:48 +08:00
|
|
|
? (key: string, val: unknown) => {
|
2019-05-30 23:16:15 +08:00
|
|
|
props[key] = val
|
|
|
|
propsProxy[key] = val
|
|
|
|
}
|
2019-10-22 23:26:48 +08:00
|
|
|
: (key: string, val: unknown) => {
|
2019-05-30 23:16:15 +08:00
|
|
|
props[key] = val
|
|
|
|
}
|
|
|
|
|
2019-08-23 21:38:32 +08:00
|
|
|
// allow mutation of propsProxy (which is readonly by default)
|
2019-05-30 23:16:15 +08:00
|
|
|
unlock()
|
|
|
|
|
2019-05-29 09:18:45 +08:00
|
|
|
if (rawProps != null) {
|
|
|
|
for (const key in rawProps) {
|
2019-11-21 10:56:17 +08:00
|
|
|
// key, ref are reserved and never passed down
|
|
|
|
if (key === 'key' || key === 'ref') continue
|
2019-10-24 22:59:57 +08:00
|
|
|
// prop option names are camelized during normalization, so to support
|
|
|
|
// kebab -> camel conversion here we need to camelize the key.
|
2020-01-07 04:05:57 +08:00
|
|
|
if (hasDeclaredProps) {
|
|
|
|
const camelKey = camelize(key)
|
|
|
|
if (hasOwn(options, camelKey)) {
|
|
|
|
setProp(camelKey, rawProps[key])
|
|
|
|
} else {
|
|
|
|
// Any non-declared props are put into a separate `attrs` object
|
|
|
|
// for spreading. Make sure to preserve original key casing
|
|
|
|
;(attrs || (attrs = {}))[key] = rawProps[key]
|
|
|
|
}
|
2019-05-28 18:06:00 +08:00
|
|
|
} else {
|
2020-01-07 04:05:57 +08:00
|
|
|
setProp(key, rawProps[key])
|
2019-05-28 18:06:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (hasDeclaredProps) {
|
2019-12-13 11:07:48 +08:00
|
|
|
// set default values & cast booleans
|
|
|
|
for (let i = 0; i < needCastKeys.length; i++) {
|
|
|
|
const key = needCastKeys[i]
|
2019-05-28 18:06:00 +08:00
|
|
|
let opt = options[key]
|
|
|
|
if (opt == null) continue
|
2019-09-06 08:48:14 +08:00
|
|
|
const isAbsent = !hasOwn(props, key)
|
|
|
|
const hasDefault = hasOwn(opt, 'default')
|
2019-05-28 18:06:00 +08:00
|
|
|
const currentValue = props[key]
|
|
|
|
// default values
|
|
|
|
if (hasDefault && currentValue === undefined) {
|
|
|
|
const defaultValue = opt.default
|
2019-05-30 23:16:15 +08:00
|
|
|
setProp(key, isFunction(defaultValue) ? defaultValue() : defaultValue)
|
2019-05-28 18:06:00 +08:00
|
|
|
}
|
|
|
|
// boolean casting
|
|
|
|
if (opt[BooleanFlags.shouldCast]) {
|
|
|
|
if (isAbsent && !hasDefault) {
|
2019-05-30 23:16:15 +08:00
|
|
|
setProp(key, false)
|
2019-05-28 18:06:00 +08:00
|
|
|
} else if (
|
|
|
|
opt[BooleanFlags.shouldCastTrue] &&
|
|
|
|
(currentValue === '' || currentValue === hyphenate(key))
|
|
|
|
) {
|
2019-05-30 23:16:15 +08:00
|
|
|
setProp(key, true)
|
2019-05-28 18:06:00 +08:00
|
|
|
}
|
|
|
|
}
|
2019-12-13 11:07:48 +08:00
|
|
|
}
|
|
|
|
// validation
|
|
|
|
if (__DEV__ && rawProps) {
|
|
|
|
for (const key in options) {
|
|
|
|
let opt = options[key]
|
|
|
|
if (opt == null) continue
|
2019-10-24 22:59:57 +08:00
|
|
|
let rawValue
|
|
|
|
if (!(key in rawProps) && hyphenate(key) in rawProps) {
|
2019-10-24 09:51:01 +08:00
|
|
|
rawValue = rawProps[hyphenate(key)]
|
2019-10-24 22:59:57 +08:00
|
|
|
} else {
|
|
|
|
rawValue = rawProps[key]
|
2019-10-24 09:51:01 +08:00
|
|
|
}
|
2019-12-13 11:07:48 +08:00
|
|
|
validateProp(key, toRaw(rawValue), opt, !hasOwn(props, key))
|
2019-05-28 18:06:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// if component has no declared props, $attrs === $props
|
|
|
|
attrs = props
|
|
|
|
}
|
2019-05-29 11:36:16 +08:00
|
|
|
|
2019-06-05 21:38:32 +08:00
|
|
|
// in case of dynamic props, check if we need to delete keys from
|
|
|
|
// the props proxy
|
|
|
|
const { patchFlag } = instance.vnode
|
2019-08-22 23:12:37 +08:00
|
|
|
if (
|
|
|
|
propsProxy !== null &&
|
|
|
|
(patchFlag === 0 || patchFlag & PatchFlags.FULL_PROPS)
|
|
|
|
) {
|
2019-06-11 23:50:28 +08:00
|
|
|
const rawInitialProps = toRaw(propsProxy)
|
2019-06-05 21:38:32 +08:00
|
|
|
for (const key in rawInitialProps) {
|
2019-09-06 08:48:14 +08:00
|
|
|
if (!hasOwn(props, key)) {
|
2019-06-05 21:38:32 +08:00
|
|
|
delete propsProxy[key]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-23 21:38:32 +08:00
|
|
|
// lock readonly
|
2019-05-30 23:16:15 +08:00
|
|
|
lock()
|
|
|
|
|
2019-11-07 01:51:06 +08:00
|
|
|
instance.props = props
|
|
|
|
instance.attrs = options ? attrs || EMPTY_OBJ : props
|
2019-05-28 18:06:00 +08:00
|
|
|
}
|
|
|
|
|
2019-12-13 11:07:48 +08:00
|
|
|
const normalizationMap = new WeakMap<
|
|
|
|
ComponentPropsOptions,
|
|
|
|
NormalizedPropsOptions
|
|
|
|
>()
|
2019-05-28 18:06:00 +08:00
|
|
|
|
|
|
|
function normalizePropsOptions(
|
|
|
|
raw: ComponentPropsOptions | void
|
2019-12-13 11:07:48 +08:00
|
|
|
): NormalizedPropsOptions {
|
2019-05-28 18:06:00 +08:00
|
|
|
if (!raw) {
|
2019-12-13 11:07:48 +08:00
|
|
|
return [] as any
|
2019-05-28 18:06:00 +08:00
|
|
|
}
|
|
|
|
if (normalizationMap.has(raw)) {
|
2019-12-13 11:07:48 +08:00
|
|
|
return normalizationMap.get(raw)!
|
2019-05-28 18:06:00 +08:00
|
|
|
}
|
2019-12-13 11:07:48 +08:00
|
|
|
const options: NormalizedPropsOptions[0] = {}
|
|
|
|
const needCastKeys: NormalizedPropsOptions[1] = []
|
2019-05-28 18:06:00 +08:00
|
|
|
if (isArray(raw)) {
|
|
|
|
for (let i = 0; i < raw.length; i++) {
|
|
|
|
if (__DEV__ && !isString(raw[i])) {
|
|
|
|
warn(`props must be strings when using array syntax.`, raw[i])
|
|
|
|
}
|
|
|
|
const normalizedKey = camelize(raw[i])
|
2019-06-01 17:43:41 +08:00
|
|
|
if (normalizedKey[0] !== '$') {
|
2019-12-13 11:07:48 +08:00
|
|
|
options[normalizedKey] = EMPTY_OBJ
|
2019-05-28 18:06:00 +08:00
|
|
|
} else if (__DEV__) {
|
|
|
|
warn(`Invalid prop name: "${normalizedKey}" is a reserved property.`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (__DEV__ && !isObject(raw)) {
|
|
|
|
warn(`invalid props options`, raw)
|
|
|
|
}
|
|
|
|
for (const key in raw) {
|
|
|
|
const normalizedKey = camelize(key)
|
2019-06-01 17:43:41 +08:00
|
|
|
if (normalizedKey[0] !== '$') {
|
2019-05-28 18:06:00 +08:00
|
|
|
const opt = raw[key]
|
2019-12-13 11:07:48 +08:00
|
|
|
const prop: NormalizedProp = (options[normalizedKey] =
|
2019-05-28 18:06:00 +08:00
|
|
|
isArray(opt) || isFunction(opt) ? { type: opt } : opt)
|
2019-06-01 00:47:05 +08:00
|
|
|
if (prop != null) {
|
2019-05-28 18:06:00 +08:00
|
|
|
const booleanIndex = getTypeIndex(Boolean, prop.type)
|
|
|
|
const stringIndex = getTypeIndex(String, prop.type)
|
2019-06-01 00:47:05 +08:00
|
|
|
prop[BooleanFlags.shouldCast] = booleanIndex > -1
|
|
|
|
prop[BooleanFlags.shouldCastTrue] = booleanIndex < stringIndex
|
2019-12-13 11:07:48 +08:00
|
|
|
// if the prop needs boolean casting or default value
|
|
|
|
if (booleanIndex > -1 || hasOwn(prop, 'default')) {
|
|
|
|
needCastKeys.push(normalizedKey)
|
|
|
|
}
|
2019-05-28 18:06:00 +08:00
|
|
|
}
|
|
|
|
} else if (__DEV__) {
|
|
|
|
warn(`Invalid prop name: "${normalizedKey}" is a reserved property.`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-12-13 11:07:48 +08:00
|
|
|
const normalized: NormalizedPropsOptions = [options, needCastKeys]
|
|
|
|
normalizationMap.set(raw, normalized)
|
2019-05-28 18:06:00 +08:00
|
|
|
return normalized
|
|
|
|
}
|
|
|
|
|
|
|
|
// use function string name to check type constructors
|
|
|
|
// so that it works across vms / iframes.
|
|
|
|
function getType(ctor: Prop<any>): string {
|
|
|
|
const match = ctor && ctor.toString().match(/^\s*function (\w+)/)
|
|
|
|
return match ? match[1] : ''
|
|
|
|
}
|
|
|
|
|
|
|
|
function isSameType(a: Prop<any>, b: Prop<any>): boolean {
|
|
|
|
return getType(a) === getType(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
function getTypeIndex(
|
|
|
|
type: Prop<any>,
|
|
|
|
expectedTypes: PropType<any> | void | null | true
|
|
|
|
): number {
|
|
|
|
if (isArray(expectedTypes)) {
|
|
|
|
for (let i = 0, len = expectedTypes.length; i < len; i++) {
|
|
|
|
if (isSameType(expectedTypes[i], type)) {
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (isObject(expectedTypes)) {
|
|
|
|
return isSameType(expectedTypes, type) ? 0 : -1
|
|
|
|
}
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
type AssertionResult = {
|
|
|
|
valid: boolean
|
|
|
|
expectedType: string
|
|
|
|
}
|
|
|
|
|
|
|
|
function validateProp(
|
|
|
|
name: string,
|
2019-10-22 23:26:48 +08:00
|
|
|
value: unknown,
|
|
|
|
prop: PropOptions,
|
2019-05-28 18:06:00 +08:00
|
|
|
isAbsent: boolean
|
|
|
|
) {
|
|
|
|
const { type, required, validator } = prop
|
|
|
|
// required!
|
|
|
|
if (required && isAbsent) {
|
|
|
|
warn('Missing required prop: "' + name + '"')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// missing but optional
|
|
|
|
if (value == null && !prop.required) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// type check
|
|
|
|
if (type != null && type !== true) {
|
|
|
|
let isValid = false
|
|
|
|
const types = isArray(type) ? type : [type]
|
|
|
|
const expectedTypes = []
|
|
|
|
// value is valid as long as one of the specified types match
|
|
|
|
for (let i = 0; i < types.length && !isValid; i++) {
|
|
|
|
const { valid, expectedType } = assertType(value, types[i])
|
|
|
|
expectedTypes.push(expectedType || '')
|
|
|
|
isValid = valid
|
|
|
|
}
|
|
|
|
if (!isValid) {
|
|
|
|
warn(getInvalidTypeMessage(name, value, expectedTypes))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// custom validator
|
|
|
|
if (validator && !validator(value)) {
|
|
|
|
warn('Invalid prop: custom validator check failed for prop "' + name + '".')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-23 22:34:58 +08:00
|
|
|
const isSimpleType = /*#__PURE__*/ makeMap(
|
|
|
|
'String,Number,Boolean,Function,Symbol'
|
|
|
|
)
|
2019-05-28 18:06:00 +08:00
|
|
|
|
2019-10-22 23:26:48 +08:00
|
|
|
function assertType(value: unknown, type: PropConstructor): AssertionResult {
|
2019-05-28 18:06:00 +08:00
|
|
|
let valid
|
|
|
|
const expectedType = getType(type)
|
2019-10-23 22:34:58 +08:00
|
|
|
if (isSimpleType(expectedType)) {
|
2019-05-28 18:06:00 +08:00
|
|
|
const t = typeof value
|
|
|
|
valid = t === expectedType.toLowerCase()
|
|
|
|
// for primitive wrapper objects
|
|
|
|
if (!valid && t === 'object') {
|
|
|
|
valid = value instanceof type
|
|
|
|
}
|
|
|
|
} else if (expectedType === 'Object') {
|
|
|
|
valid = toRawType(value) === 'Object'
|
|
|
|
} else if (expectedType === 'Array') {
|
|
|
|
valid = isArray(value)
|
|
|
|
} else {
|
|
|
|
valid = value instanceof type
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
valid,
|
|
|
|
expectedType
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getInvalidTypeMessage(
|
|
|
|
name: string,
|
2019-10-22 23:26:48 +08:00
|
|
|
value: unknown,
|
2019-05-28 18:06:00 +08:00
|
|
|
expectedTypes: string[]
|
|
|
|
): string {
|
|
|
|
let message =
|
|
|
|
`Invalid prop: type check failed for prop "${name}".` +
|
|
|
|
` Expected ${expectedTypes.map(capitalize).join(', ')}`
|
|
|
|
const expectedType = expectedTypes[0]
|
|
|
|
const receivedType = toRawType(value)
|
|
|
|
const expectedValue = styleValue(value, expectedType)
|
|
|
|
const receivedValue = styleValue(value, receivedType)
|
|
|
|
// check if we need to specify expected value
|
|
|
|
if (
|
|
|
|
expectedTypes.length === 1 &&
|
|
|
|
isExplicable(expectedType) &&
|
|
|
|
!isBoolean(expectedType, receivedType)
|
|
|
|
) {
|
|
|
|
message += ` with value ${expectedValue}`
|
|
|
|
}
|
|
|
|
message += `, got ${receivedType} `
|
|
|
|
// check if we need to specify received value
|
|
|
|
if (isExplicable(receivedType)) {
|
|
|
|
message += `with value ${receivedValue}.`
|
|
|
|
}
|
|
|
|
return message
|
|
|
|
}
|
|
|
|
|
2019-10-22 23:26:48 +08:00
|
|
|
function styleValue(value: unknown, type: string): string {
|
2019-05-28 18:06:00 +08:00
|
|
|
if (type === 'String') {
|
|
|
|
return `"${value}"`
|
|
|
|
} else if (type === 'Number') {
|
|
|
|
return `${Number(value)}`
|
|
|
|
} else {
|
|
|
|
return `${value}`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function isExplicable(type: string): boolean {
|
|
|
|
const explicitTypes = ['string', 'number', 'boolean']
|
|
|
|
return explicitTypes.some(elem => type.toLowerCase() === elem)
|
|
|
|
}
|
|
|
|
|
|
|
|
function isBoolean(...args: string[]): boolean {
|
|
|
|
return args.some(elem => elem.toLowerCase() === 'boolean')
|
|
|
|
}
|