| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820 |
- import {
- TriggerOpTypes,
- shallowReactive,
- shallowReadonly,
- toRaw,
- trigger,
- } from '@vue/reactivity'
- import {
- EMPTY_ARR,
- EMPTY_OBJ,
- type IfAny,
- PatchFlags,
- camelize,
- capitalize,
- extend,
- hasOwn,
- hyphenate,
- isArray,
- isFunction,
- isObject,
- isOn,
- isReservedProp,
- isString,
- isSymbol,
- makeMap,
- stringifySymbol,
- toRawType,
- } from '@vue/shared'
- import { warn } from './warning'
- import {
- type ComponentInternalInstance,
- type ComponentOptions,
- type ConcreteComponent,
- type Data,
- setCurrentInstance,
- } from './component'
- import { isEmitListener } from './componentEmits'
- import type { AppContext } from './apiCreateApp'
- import { createPropsDefaultThis } from './compat/props'
- import { isCompatEnabled, softAssertCompatEnabled } from './compat/compatConfig'
- import { DeprecationTypes } from './compat/compatConfig'
- import { shouldSkipAttr } from './compat/attrsFallthrough'
- import { createInternalObject } from './internalObject'
- export type ComponentPropsOptions<P = Data> =
- | ComponentObjectPropsOptions<P>
- | string[]
- export type ComponentObjectPropsOptions<P = Data> = {
- [K in keyof P]: Prop<P[K]> | null
- }
- export type Prop<T, D = T> = PropOptions<T, D> | PropType<T>
- type DefaultFactory<T> = (props: Data) => T | null | undefined
- export interface PropOptions<T = any, D = T> {
- type?: PropType<T> | true | null
- required?: boolean
- default?: D | DefaultFactory<D> | null | undefined | object
- validator?(value: unknown, props: Data): boolean
- /**
- * @internal
- */
- skipCheck?: boolean
- /**
- * @internal
- */
- skipFactory?: boolean
- }
- export type PropType<T> = PropConstructor<T> | (PropConstructor<T> | null)[]
- type PropConstructor<T = any> =
- | { new (...args: any[]): T & {} }
- | { (): T }
- | PropMethod<T>
- type PropMethod<T, TConstructor = any> = [T] extends [
- ((...args: any) => any) | undefined,
- ] // if is function with args, allowing non-required functions
- ? { new (): TConstructor; (): T; readonly prototype: TConstructor } // Create Function like constructor
- : never
- type RequiredKeys<T> = {
- [K in keyof T]: T[K] extends
- | { required: true }
- | { default: any }
- // don't mark Boolean props as undefined
- | BooleanConstructor
- | { type: BooleanConstructor }
- ? T[K] extends { default: undefined | (() => undefined) }
- ? never
- : K
- : never
- }[keyof T]
- type OptionalKeys<T> = Exclude<keyof T, RequiredKeys<T>>
- type DefaultKeys<T> = {
- [K in keyof T]: T[K] extends
- | { default: any }
- // Boolean implicitly defaults to false
- | BooleanConstructor
- | { type: BooleanConstructor }
- ? T[K] extends { type: BooleanConstructor; required: true } // not default if Boolean is marked as required
- ? never
- : K
- : never
- }[keyof T]
- type InferPropType<T, NullAsAny = true> = [T] extends [null]
- ? NullAsAny extends true
- ? any
- : null
- : [T] extends [{ type: null | true }]
- ? any // As TS issue https://github.com/Microsoft/TypeScript/issues/14829 // somehow `ObjectConstructor` when inferred from { (): T } becomes `any` // `BooleanConstructor` when inferred from PropConstructor(with PropMethod) becomes `Boolean`
- : [T] extends [ObjectConstructor | { type: ObjectConstructor }]
- ? Record<string, any>
- : [T] extends [BooleanConstructor | { type: BooleanConstructor }]
- ? boolean
- : [T] extends [DateConstructor | { type: DateConstructor }]
- ? Date
- : [T] extends [(infer U)[] | { type: (infer U)[] }]
- ? U extends DateConstructor
- ? Date | InferPropType<U, false>
- : InferPropType<U, false>
- : [T] extends [Prop<infer V, infer D>]
- ? unknown extends V
- ? keyof V extends never
- ? IfAny<V, V, D>
- : V
- : V
- : T
- /**
- * Extract prop types from a runtime props options object.
- * The extracted types are **internal** - i.e. the resolved props received by
- * the component.
- * - Boolean props are always present
- * - Props with default values are always present
- *
- * To extract accepted props from the parent, use {@link ExtractPublicPropTypes}.
- */
- export type ExtractPropTypes<O> = {
- // use `keyof Pick<O, RequiredKeys<O>>` instead of `RequiredKeys<O>` to
- // support IDE features
- [K in keyof Pick<O, RequiredKeys<O>>]: InferPropType<O[K]>
- } & {
- // use `keyof Pick<O, OptionalKeys<O>>` instead of `OptionalKeys<O>` to
- // support IDE features
- [K in keyof Pick<O, OptionalKeys<O>>]?: InferPropType<O[K]>
- }
- type PublicRequiredKeys<T> = {
- [K in keyof T]: T[K] extends { required: true } ? K : never
- }[keyof T]
- type PublicOptionalKeys<T> = Exclude<keyof T, PublicRequiredKeys<T>>
- /**
- * Extract prop types from a runtime props options object.
- * The extracted types are **public** - i.e. the expected props that can be
- * passed to component.
- */
- export type ExtractPublicPropTypes<O> = {
- [K in keyof Pick<O, PublicRequiredKeys<O>>]: InferPropType<O[K]>
- } & {
- [K in keyof Pick<O, PublicOptionalKeys<O>>]?: InferPropType<O[K]>
- }
- enum BooleanFlags {
- shouldCast,
- shouldCastTrue,
- }
- // extract props which defined with default from prop options
- export type ExtractDefaultPropTypes<O> = O extends object
- ? // use `keyof Pick<O, DefaultKeys<O>>` instead of `DefaultKeys<O>` to support IDE features
- { [K in keyof Pick<O, DefaultKeys<O>>]: InferPropType<O[K]> }
- : {}
- type NormalizedProp = PropOptions & {
- [BooleanFlags.shouldCast]?: boolean
- [BooleanFlags.shouldCastTrue]?: boolean
- }
- // normalized value is a tuple of the actual normalized options
- // and an array of prop keys that need value casting (booleans and defaults)
- export type NormalizedProps = Record<string, NormalizedProp>
- export type NormalizedPropsOptions = [NormalizedProps, string[]] | []
- export function initProps(
- instance: ComponentInternalInstance,
- rawProps: Data | null,
- isStateful: number, // result of bitwise flag comparison
- isSSR = false,
- ): void {
- const props: Data = {}
- const attrs: Data = createInternalObject()
- instance.propsDefaults = Object.create(null)
- setFullProps(instance, rawProps, props, attrs)
- // ensure all declared prop keys are present
- for (const key in instance.propsOptions[0]) {
- if (!(key in props)) {
- props[key] = undefined
- }
- }
- // validation
- if (__DEV__) {
- validateProps(rawProps || {}, props, instance)
- }
- if (isStateful) {
- // stateful
- instance.props = isSSR ? props : shallowReactive(props)
- } else {
- if (!instance.type.props) {
- // functional w/ optional props, props === attrs
- instance.props = attrs
- } else {
- // functional w/ declared props
- instance.props = props
- }
- }
- instance.attrs = attrs
- }
- function isInHmrContext(instance: ComponentInternalInstance | null) {
- while (instance) {
- if (instance.type.__hmrId) return true
- instance = instance.parent
- }
- }
- export function updateProps(
- instance: ComponentInternalInstance,
- rawProps: Data | null,
- rawPrevProps: Data | null,
- optimized: boolean,
- ): void {
- const {
- props,
- attrs,
- vnode: { patchFlag },
- } = instance
- const rawCurrentProps = toRaw(props)
- const [options] = instance.propsOptions
- let hasAttrsChanged = false
- if (
- // always force full diff in dev
- // - #1942 if hmr is enabled with sfc component
- // - vite#872 non-sfc component used by sfc component
- !(__DEV__ && isInHmrContext(instance)) &&
- (optimized || patchFlag > 0) &&
- !(patchFlag & PatchFlags.FULL_PROPS)
- ) {
- if (patchFlag & PatchFlags.PROPS) {
- // Compiler-generated props & no keys change, just set the updated
- // the props.
- const propsToUpdate = instance.vnode.dynamicProps!
- for (let i = 0; i < propsToUpdate.length; i++) {
- let key = propsToUpdate[i]
- // skip if the prop key is a declared emit event listener
- if (isEmitListener(instance.emitsOptions, key)) {
- continue
- }
- // PROPS flag guarantees rawProps to be non-null
- const value = rawProps![key]
- if (options) {
- // attr / props separation was done on init and will be consistent
- // in this code path, so just check if attrs have it.
- if (hasOwn(attrs, key)) {
- if (value !== attrs[key]) {
- attrs[key] = value
- hasAttrsChanged = true
- }
- } else {
- const camelizedKey = camelize(key)
- props[camelizedKey] = resolvePropValue(
- options,
- rawCurrentProps,
- camelizedKey,
- value,
- instance,
- false /* isAbsent */,
- )
- }
- } else {
- if (__COMPAT__) {
- if (isOn(key) && key.endsWith('Native')) {
- key = key.slice(0, -6) // remove Native postfix
- } else if (shouldSkipAttr(key, instance)) {
- continue
- }
- }
- if (value !== attrs[key]) {
- attrs[key] = value
- hasAttrsChanged = true
- }
- }
- }
- }
- } else {
- // full props update.
- if (setFullProps(instance, rawProps, props, attrs)) {
- hasAttrsChanged = true
- }
- // in case of dynamic props, check if we need to delete keys from
- // the props object
- let kebabKey: string
- for (const key in rawCurrentProps) {
- if (
- !rawProps ||
- // for camelCase
- (!hasOwn(rawProps, key) &&
- // it's possible the original props was passed in as kebab-case
- // and converted to camelCase (#955)
- ((kebabKey = hyphenate(key)) === key || !hasOwn(rawProps, kebabKey)))
- ) {
- if (options) {
- if (
- rawPrevProps &&
- // for camelCase
- (rawPrevProps[key] !== undefined ||
- // for kebab-case
- rawPrevProps[kebabKey!] !== undefined)
- ) {
- props[key] = resolvePropValue(
- options,
- rawCurrentProps,
- key,
- undefined,
- instance,
- true /* isAbsent */,
- )
- }
- } else {
- delete props[key]
- }
- }
- }
- // in the case of functional component w/o props declaration, props and
- // attrs point to the same object so it should already have been updated.
- if (attrs !== rawCurrentProps) {
- for (const key in attrs) {
- if (
- !rawProps ||
- (!hasOwn(rawProps, key) &&
- (!__COMPAT__ || !hasOwn(rawProps, key + 'Native')))
- ) {
- delete attrs[key]
- hasAttrsChanged = true
- }
- }
- }
- }
- // trigger updates for $attrs in case it's used in component slots
- if (hasAttrsChanged) {
- trigger(instance.attrs, TriggerOpTypes.SET, '')
- }
- if (__DEV__) {
- validateProps(rawProps || {}, props, instance)
- }
- }
- function setFullProps(
- instance: ComponentInternalInstance,
- rawProps: Data | null,
- props: Data,
- attrs: Data,
- ) {
- const [options, needCastKeys] = instance.propsOptions
- let hasAttrsChanged = false
- let rawCastValues: Data | undefined
- if (rawProps) {
- for (let key in rawProps) {
- // key, ref are reserved and never passed down
- if (isReservedProp(key)) {
- continue
- }
- if (__COMPAT__) {
- if (key.startsWith('onHook:')) {
- softAssertCompatEnabled(
- DeprecationTypes.INSTANCE_EVENT_HOOKS,
- instance,
- key.slice(2).toLowerCase(),
- )
- }
- if (key === 'inline-template') {
- continue
- }
- }
- const value = rawProps[key]
- // prop option names are camelized during normalization, so to support
- // kebab -> camel conversion here we need to camelize the key.
- let camelKey
- if (options && hasOwn(options, (camelKey = camelize(key)))) {
- if (!needCastKeys || !needCastKeys.includes(camelKey)) {
- props[camelKey] = value
- } else {
- ;(rawCastValues || (rawCastValues = {}))[camelKey] = value
- }
- } else if (!isEmitListener(instance.emitsOptions, key)) {
- // Any non-declared (either as a prop or an emitted event) props are put
- // into a separate `attrs` object for spreading. Make sure to preserve
- // original key casing
- if (__COMPAT__) {
- if (isOn(key) && key.endsWith('Native')) {
- key = key.slice(0, -6) // remove Native postfix
- } else if (shouldSkipAttr(key, instance)) {
- continue
- }
- }
- if (!(key in attrs) || value !== attrs[key]) {
- attrs[key] = value
- hasAttrsChanged = true
- }
- }
- }
- }
- if (needCastKeys) {
- const rawCurrentProps = toRaw(props)
- const castValues = rawCastValues || EMPTY_OBJ
- for (let i = 0; i < needCastKeys.length; i++) {
- const key = needCastKeys[i]
- props[key] = resolvePropValue(
- options!,
- rawCurrentProps,
- key,
- castValues[key],
- instance,
- !hasOwn(castValues, key),
- )
- }
- }
- return hasAttrsChanged
- }
- function resolvePropValue(
- options: NormalizedProps,
- props: Data,
- key: string,
- value: unknown,
- instance: ComponentInternalInstance,
- isAbsent: boolean,
- ) {
- const opt = options[key]
- if (opt != null) {
- const hasDefault = hasOwn(opt, 'default')
- // default values
- if (hasDefault && value === undefined) {
- const defaultValue = opt.default
- if (
- opt.type !== Function &&
- !opt.skipFactory &&
- isFunction(defaultValue)
- ) {
- const { propsDefaults } = instance
- if (key in propsDefaults) {
- value = propsDefaults[key]
- } else {
- const reset = setCurrentInstance(instance)
- value = propsDefaults[key] = defaultValue.call(
- __COMPAT__ &&
- isCompatEnabled(DeprecationTypes.PROPS_DEFAULT_THIS, instance)
- ? createPropsDefaultThis(instance, props, key)
- : null,
- props,
- )
- reset()
- }
- } else {
- value = defaultValue
- }
- // #9006 reflect default value on custom element
- if (instance.ce) {
- instance.ce._setProp(key, value)
- }
- }
- // boolean casting
- if (opt[BooleanFlags.shouldCast]) {
- if (isAbsent && !hasDefault) {
- value = false
- } else if (
- opt[BooleanFlags.shouldCastTrue] &&
- (value === '' || value === hyphenate(key))
- ) {
- value = true
- }
- }
- }
- return value
- }
- const mixinPropsCache = new WeakMap<ConcreteComponent, NormalizedPropsOptions>()
- export function normalizePropsOptions(
- comp: ConcreteComponent,
- appContext: AppContext,
- asMixin = false,
- ): NormalizedPropsOptions {
- const cache =
- __FEATURE_OPTIONS_API__ && asMixin ? mixinPropsCache : appContext.propsCache
- const cached = cache.get(comp)
- if (cached) {
- return cached
- }
- const raw = comp.props
- const normalized: NormalizedPropsOptions[0] = {}
- const needCastKeys: NormalizedPropsOptions[1] = []
- // apply mixin/extends props
- let hasExtends = false
- if (__FEATURE_OPTIONS_API__ && !isFunction(comp)) {
- const extendProps = (raw: ComponentOptions) => {
- if (__COMPAT__ && isFunction(raw)) {
- raw = raw.options
- }
- hasExtends = true
- const [props, keys] = normalizePropsOptions(raw, appContext, true)
- extend(normalized, props)
- if (keys) needCastKeys.push(...keys)
- }
- if (!asMixin && appContext.mixins.length) {
- appContext.mixins.forEach(extendProps)
- }
- if (comp.extends) {
- extendProps(comp.extends)
- }
- if (comp.mixins) {
- comp.mixins.forEach(extendProps)
- }
- }
- if (!raw && !hasExtends) {
- if (isObject(comp)) {
- cache.set(comp, EMPTY_ARR as any)
- }
- return EMPTY_ARR as any
- }
- 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])
- if (validatePropName(normalizedKey)) {
- normalized[normalizedKey] = EMPTY_OBJ
- }
- }
- } else if (raw) {
- if (__DEV__ && !isObject(raw)) {
- warn(`invalid props options`, raw)
- }
- for (const key in raw) {
- const normalizedKey = camelize(key)
- if (validatePropName(normalizedKey)) {
- const opt = raw[key]
- const prop: NormalizedProp = (normalized[normalizedKey] =
- isArray(opt) || isFunction(opt) ? { type: opt } : extend({}, opt))
- const propType = prop.type
- let shouldCast = false
- let shouldCastTrue = true
- if (isArray(propType)) {
- for (let index = 0; index < propType.length; ++index) {
- const type = propType[index]
- const typeName = isFunction(type) && type.name
- if (typeName === 'Boolean') {
- shouldCast = true
- break
- } else if (typeName === 'String') {
- // If we find `String` before `Boolean`, e.g. `[String, Boolean]`,
- // we need to handle the casting slightly differently. Props
- // passed as `<Comp checked="">` or `<Comp checked="checked">`
- // will either be treated as strings or converted to a boolean
- // `true`, depending on the order of the types.
- shouldCastTrue = false
- }
- }
- } else {
- shouldCast = isFunction(propType) && propType.name === 'Boolean'
- }
- prop[BooleanFlags.shouldCast] = shouldCast
- prop[BooleanFlags.shouldCastTrue] = shouldCastTrue
- // if the prop needs boolean casting or default value
- if (shouldCast || hasOwn(prop, 'default')) {
- needCastKeys.push(normalizedKey)
- }
- }
- }
- }
- const res: NormalizedPropsOptions = [normalized, needCastKeys]
- if (isObject(comp)) {
- cache.set(comp, res)
- }
- return res
- }
- function validatePropName(key: string) {
- if (key[0] !== '$' && !isReservedProp(key)) {
- return true
- } else if (__DEV__) {
- warn(`Invalid prop name: "${key}" is a reserved property.`)
- }
- return false
- }
- // dev only
- // use function string name to check type constructors
- // so that it works across vms / iframes.
- function getType(ctor: Prop<any> | null): string {
- // Early return for null to avoid unnecessary computations
- if (ctor === null) {
- return 'null'
- }
- // Avoid using regex for common cases by checking the type directly
- if (typeof ctor === 'function') {
- // Using name property to avoid converting function to string
- return ctor.name || ''
- } else if (typeof ctor === 'object') {
- // Attempting to directly access constructor name if possible
- const name = ctor.constructor && ctor.constructor.name
- return name || ''
- }
- // Fallback for other types (though they're less likely to have meaningful names here)
- return ''
- }
- /**
- * dev only
- */
- function validateProps(
- rawProps: Data,
- props: Data,
- instance: ComponentInternalInstance,
- ) {
- const resolvedValues = toRaw(props)
- const options = instance.propsOptions[0]
- const camelizePropsKey = Object.keys(rawProps).map(key => camelize(key))
- for (const key in options) {
- let opt = options[key]
- if (opt == null) continue
- validateProp(
- key,
- resolvedValues[key],
- opt,
- __DEV__ ? shallowReadonly(resolvedValues) : resolvedValues,
- !camelizePropsKey.includes(key),
- )
- }
- }
- /**
- * dev only
- */
- function validateProp(
- name: string,
- value: unknown,
- prop: PropOptions,
- props: Data,
- isAbsent: boolean,
- ) {
- const { type, required, validator, skipCheck } = prop
- // required!
- if (required && isAbsent) {
- warn('Missing required prop: "' + name + '"')
- return
- }
- // missing but optional
- if (value == null && !required) {
- return
- }
- // type check
- if (type != null && type !== true && !skipCheck) {
- 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, props)) {
- warn('Invalid prop: custom validator check failed for prop "' + name + '".')
- }
- }
- const isSimpleType = /*@__PURE__*/ makeMap(
- 'String,Number,Boolean,Function,Symbol,BigInt',
- )
- type AssertionResult = {
- valid: boolean
- expectedType: string
- }
- /**
- * dev only
- */
- function assertType(
- value: unknown,
- type: PropConstructor | null,
- ): AssertionResult {
- let valid
- const expectedType = getType(type)
- if (expectedType === 'null') {
- valid = value === null
- } else if (isSimpleType(expectedType)) {
- const t = typeof value
- valid = t === expectedType.toLowerCase()
- // for primitive wrapper objects
- if (!valid && t === 'object') {
- valid = value instanceof (type as PropConstructor)
- }
- } else if (expectedType === 'Object') {
- valid = isObject(value)
- } else if (expectedType === 'Array') {
- valid = isArray(value)
- } else {
- valid = value instanceof (type as PropConstructor)
- }
- return {
- valid,
- expectedType,
- }
- }
- /**
- * dev only
- */
- function getInvalidTypeMessage(
- name: string,
- value: unknown,
- expectedTypes: string[],
- ): string {
- if (expectedTypes.length === 0) {
- return (
- `Prop type [] for prop "${name}" won't match anything.` +
- ` Did you mean to use type Array instead?`
- )
- }
- 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
- }
- /**
- * dev only
- */
- function styleValue(value: unknown, type: string): string {
- if (type === 'String') {
- return `"${value}"`
- } else if (type === 'Number') {
- return `${Number(value)}`
- } else if (isSymbol(value)) {
- return stringifySymbol(value)
- } else {
- return `${value}`
- }
- }
- /**
- * dev only
- */
- function isExplicable(type: string): boolean {
- const explicitTypes = ['string', 'number', 'boolean']
- return explicitTypes.some(elem => type.toLowerCase() === elem)
- }
- /**
- * dev only
- */
- function isBoolean(...args: string[]): boolean {
- return args.some(elem => elem.toLowerCase() === 'boolean')
- }
|