componentProps.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  1. import {
  2. toRaw,
  3. shallowReactive,
  4. trigger,
  5. TriggerOpTypes
  6. } from '@vue/reactivity'
  7. import {
  8. EMPTY_OBJ,
  9. camelize,
  10. hyphenate,
  11. capitalize,
  12. isString,
  13. isFunction,
  14. isArray,
  15. isObject,
  16. hasOwn,
  17. toRawType,
  18. PatchFlags,
  19. makeMap,
  20. isReservedProp,
  21. EMPTY_ARR,
  22. def,
  23. extend,
  24. isOn,
  25. IfAny
  26. } from '@vue/shared'
  27. import { warn } from './warning'
  28. import {
  29. Data,
  30. ComponentInternalInstance,
  31. ComponentOptions,
  32. ConcreteComponent,
  33. setCurrentInstance,
  34. unsetCurrentInstance
  35. } from './component'
  36. import { isEmitListener } from './componentEmits'
  37. import { InternalObjectKey } from './vnode'
  38. import { AppContext } from './apiCreateApp'
  39. import { createPropsDefaultThis } from './compat/props'
  40. import { isCompatEnabled, softAssertCompatEnabled } from './compat/compatConfig'
  41. import { DeprecationTypes } from './compat/compatConfig'
  42. import { shouldSkipAttr } from './compat/attrsFallthrough'
  43. export type ComponentPropsOptions<P = Data> =
  44. | ComponentObjectPropsOptions<P>
  45. | string[]
  46. export type ComponentObjectPropsOptions<P = Data> = {
  47. [K in keyof P]: Prop<P[K]> | null
  48. }
  49. export type Prop<T, D = T> = PropOptions<T, D> | PropType<T>
  50. type DefaultFactory<T> = (props: Data) => T | null | undefined
  51. export interface PropOptions<T = any, D = T> {
  52. type?: PropType<T> | true | null
  53. required?: boolean
  54. default?: D | DefaultFactory<D> | null | undefined | object
  55. validator?(value: unknown): boolean
  56. }
  57. export type PropType<T> = PropConstructor<T> | PropConstructor<T>[]
  58. type PropConstructor<T = any> =
  59. | { new (...args: any[]): T & {} }
  60. | { (): T }
  61. | PropMethod<T>
  62. type PropMethod<T, TConstructor = any> = [T] extends [
  63. ((...args: any) => any) | undefined
  64. ] // if is function with args, allowing non-required functions
  65. ? { new (): TConstructor; (): T; readonly prototype: TConstructor } // Create Function like constructor
  66. : never
  67. type RequiredKeys<T> = {
  68. [K in keyof T]: T[K] extends
  69. | { required: true }
  70. | { default: any }
  71. // don't mark Boolean props as undefined
  72. | BooleanConstructor
  73. | { type: BooleanConstructor }
  74. ? T[K] extends { default: undefined | (() => undefined) }
  75. ? never
  76. : K
  77. : never
  78. }[keyof T]
  79. type OptionalKeys<T> = Exclude<keyof T, RequiredKeys<T>>
  80. type DefaultKeys<T> = {
  81. [K in keyof T]: T[K] extends
  82. | { default: any }
  83. // Boolean implicitly defaults to false
  84. | BooleanConstructor
  85. | { type: BooleanConstructor }
  86. ? T[K] extends { type: BooleanConstructor; required: true } // not default if Boolean is marked as required
  87. ? never
  88. : K
  89. : never
  90. }[keyof T]
  91. type InferPropType<T> = [T] extends [null]
  92. ? any // null & true would fail to infer
  93. : [T] extends [{ type: null | true }]
  94. ? 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`
  95. : [T] extends [ObjectConstructor | { type: ObjectConstructor }]
  96. ? Record<string, any>
  97. : [T] extends [BooleanConstructor | { type: BooleanConstructor }]
  98. ? boolean
  99. : [T] extends [DateConstructor | { type: DateConstructor }]
  100. ? Date
  101. : [T] extends [(infer U)[] | { type: (infer U)[] }]
  102. ? U extends DateConstructor
  103. ? Date | InferPropType<U>
  104. : InferPropType<U>
  105. : [T] extends [Prop<infer V, infer D>]
  106. ? unknown extends V
  107. ? IfAny<V, V, D>
  108. : V
  109. : T
  110. export type ExtractPropTypes<O> = {
  111. // use `keyof Pick<O, RequiredKeys<O>>` instead of `RequiredKeys<O>` to support IDE features
  112. [K in keyof Pick<O, RequiredKeys<O>>]: InferPropType<O[K]>
  113. } & {
  114. // use `keyof Pick<O, OptionalKeys<O>>` instead of `OptionalKeys<O>` to support IDE features
  115. [K in keyof Pick<O, OptionalKeys<O>>]?: InferPropType<O[K]>
  116. }
  117. const enum BooleanFlags {
  118. shouldCast,
  119. shouldCastTrue
  120. }
  121. // extract props which defined with default from prop options
  122. export type ExtractDefaultPropTypes<O> = O extends object
  123. ? { [K in DefaultKeys<O>]: InferPropType<O[K]> }
  124. : {}
  125. type NormalizedProp =
  126. | null
  127. | (PropOptions & {
  128. [BooleanFlags.shouldCast]?: boolean
  129. [BooleanFlags.shouldCastTrue]?: boolean
  130. })
  131. // normalized value is a tuple of the actual normalized options
  132. // and an array of prop keys that need value casting (booleans and defaults)
  133. export type NormalizedProps = Record<string, NormalizedProp>
  134. export type NormalizedPropsOptions = [NormalizedProps, string[]] | []
  135. export function initProps(
  136. instance: ComponentInternalInstance,
  137. rawProps: Data | null,
  138. isStateful: number, // result of bitwise flag comparison
  139. isSSR = false
  140. ) {
  141. const props: Data = {}
  142. const attrs: Data = {}
  143. def(attrs, InternalObjectKey, 1)
  144. instance.propsDefaults = Object.create(null)
  145. setFullProps(instance, rawProps, props, attrs)
  146. // ensure all declared prop keys are present
  147. for (const key in instance.propsOptions[0]) {
  148. if (!(key in props)) {
  149. props[key] = undefined
  150. }
  151. }
  152. // validation
  153. if (__DEV__) {
  154. validateProps(rawProps || {}, props, instance)
  155. }
  156. if (isStateful) {
  157. // stateful
  158. instance.props = isSSR ? props : shallowReactive(props)
  159. } else {
  160. if (!instance.type.props) {
  161. // functional w/ optional props, props === attrs
  162. instance.props = attrs
  163. } else {
  164. // functional w/ declared props
  165. instance.props = props
  166. }
  167. }
  168. instance.attrs = attrs
  169. }
  170. export function updateProps(
  171. instance: ComponentInternalInstance,
  172. rawProps: Data | null,
  173. rawPrevProps: Data | null,
  174. optimized: boolean
  175. ) {
  176. debugger
  177. const {
  178. props,
  179. attrs,
  180. vnode: { patchFlag }
  181. } = instance
  182. const rawCurrentProps = toRaw(props)
  183. const [options] = instance.propsOptions
  184. let hasAttrsChanged = false
  185. if (
  186. // always force full diff in dev
  187. // - #1942 if hmr is enabled with sfc component
  188. // - vite#872 non-sfc component used by sfc component
  189. !(
  190. __DEV__ &&
  191. (instance.type.__hmrId ||
  192. (instance.parent && instance.parent.type.__hmrId))
  193. ) &&
  194. (optimized || patchFlag > 0) &&
  195. !(patchFlag & PatchFlags.FULL_PROPS)
  196. ) {
  197. if (patchFlag & PatchFlags.PROPS) {
  198. // Compiler-generated props & no keys change, just set the updated
  199. // the props.
  200. const propsToUpdate = instance.vnode.dynamicProps!
  201. for (let i = 0; i < propsToUpdate.length; i++) {
  202. let key = propsToUpdate[i]
  203. // skip if the prop key is a declared emit event listener
  204. if (isEmitListener(instance.emitsOptions, key)){
  205. continue
  206. }
  207. // PROPS flag guarantees rawProps to be non-null
  208. const value = rawProps![key]
  209. if (options) {
  210. // attr / props separation was done on init and will be consistent
  211. // in this code path, so just check if attrs have it.
  212. if (hasOwn(attrs, key)) {
  213. if (value !== attrs[key]) {
  214. attrs[key] = value
  215. hasAttrsChanged = true
  216. }
  217. } else {
  218. const camelizedKey = camelize(key)
  219. props[camelizedKey] = resolvePropValue(
  220. options,
  221. rawCurrentProps,
  222. camelizedKey,
  223. value,
  224. instance,
  225. false /* isAbsent */
  226. )
  227. }
  228. } else {
  229. if (__COMPAT__) {
  230. if (isOn(key) && key.endsWith('Native')) {
  231. key = key.slice(0, -6) // remove Native postfix
  232. } else if (shouldSkipAttr(key, instance)) {
  233. continue
  234. }
  235. }
  236. if (value !== attrs[key]) {
  237. attrs[key] = value
  238. hasAttrsChanged = true
  239. }
  240. }
  241. }
  242. }
  243. } else {
  244. // full props update.
  245. if (setFullProps(instance, rawProps, props, attrs)) {
  246. hasAttrsChanged = true
  247. }
  248. // in case of dynamic props, check if we need to delete keys from
  249. // the props object
  250. let kebabKey: string
  251. for (const key in rawCurrentProps) {
  252. if (
  253. !rawProps ||
  254. // for camelCase
  255. (!hasOwn(rawProps, key) &&
  256. // it's possible the original props was passed in as kebab-case
  257. // and converted to camelCase (#955)
  258. ((kebabKey = hyphenate(key)) === key || !hasOwn(rawProps, kebabKey)))
  259. ) {
  260. if (options) {
  261. if (
  262. rawPrevProps &&
  263. // for camelCase
  264. (rawPrevProps[key] !== undefined ||
  265. // for kebab-case
  266. rawPrevProps[kebabKey!] !== undefined)
  267. ) {
  268. props[key] = resolvePropValue(
  269. options,
  270. rawCurrentProps,
  271. key,
  272. undefined,
  273. instance,
  274. true /* isAbsent */
  275. )
  276. }
  277. } else {
  278. delete props[key]
  279. }
  280. }
  281. }
  282. // in the case of functional component w/o props declaration, props and
  283. // attrs point to the same object so it should already have been updated.
  284. if (attrs !== rawCurrentProps) {
  285. for (const key in attrs) {
  286. if (
  287. !rawProps ||
  288. (!hasOwn(rawProps, key) &&
  289. (!__COMPAT__ || !hasOwn(rawProps, key + 'Native')))
  290. ) {
  291. delete attrs[key]
  292. hasAttrsChanged = true
  293. }
  294. }
  295. }
  296. }
  297. // trigger updates for $attrs in case it's used in component slots
  298. if (hasAttrsChanged) {
  299. trigger(instance, TriggerOpTypes.SET, '$attrs')
  300. }
  301. if (__DEV__) {
  302. validateProps(rawProps || {}, props, instance)
  303. }
  304. }
  305. function setFullProps(
  306. instance: ComponentInternalInstance,
  307. rawProps: Data | null,
  308. props: Data,
  309. attrs: Data
  310. ) {
  311. const [options, needCastKeys] = instance.propsOptions
  312. let hasAttrsChanged = false
  313. let rawCastValues: Data | undefined
  314. if (rawProps) {
  315. for (let key in rawProps) {
  316. // key, ref are reserved and never passed down
  317. if (isReservedProp(key)) {
  318. continue
  319. }
  320. if (__COMPAT__) {
  321. if (key.startsWith('onHook:')) {
  322. softAssertCompatEnabled(
  323. DeprecationTypes.INSTANCE_EVENT_HOOKS,
  324. instance,
  325. key.slice(2).toLowerCase()
  326. )
  327. }
  328. if (key === 'inline-template') {
  329. continue
  330. }
  331. }
  332. const value = rawProps[key]
  333. // prop option names are camelized during normalization, so to support
  334. // kebab -> camel conversion here we need to camelize the key.
  335. let camelKey
  336. if (options && hasOwn(options, (camelKey = camelize(key)))) {
  337. if (!needCastKeys || !needCastKeys.includes(camelKey)) {
  338. props[camelKey] = value
  339. } else {
  340. ;(rawCastValues || (rawCastValues = {}))[camelKey] = value
  341. }
  342. } else if (!isEmitListener(instance.emitsOptions, key)) {
  343. // Any non-declared (either as a prop or an emitted event) props are put
  344. // into a separate `attrs` object for spreading. Make sure to preserve
  345. // original key casing
  346. if (__COMPAT__) {
  347. if (isOn(key) && key.endsWith('Native')) {
  348. key = key.slice(0, -6) // remove Native postfix
  349. } else if (shouldSkipAttr(key, instance)) {
  350. continue
  351. }
  352. }
  353. if (!(key in attrs) || value !== attrs[key]) {
  354. attrs[key] = value
  355. hasAttrsChanged = true
  356. }
  357. }
  358. }
  359. }
  360. if (needCastKeys) {
  361. const rawCurrentProps = toRaw(props)
  362. const castValues = rawCastValues || EMPTY_OBJ
  363. for (let i = 0; i < needCastKeys.length; i++) {
  364. const key = needCastKeys[i]
  365. props[key] = resolvePropValue(
  366. options!,
  367. rawCurrentProps,
  368. key,
  369. castValues[key],
  370. instance,
  371. !hasOwn(castValues, key)
  372. )
  373. }
  374. }
  375. return hasAttrsChanged
  376. }
  377. function resolvePropValue(
  378. options: NormalizedProps,
  379. props: Data,
  380. key: string,
  381. value: unknown,
  382. instance: ComponentInternalInstance,
  383. isAbsent: boolean
  384. ) {
  385. const opt = options[key]
  386. if (opt != null) {
  387. const hasDefault = hasOwn(opt, 'default')
  388. // default values
  389. if (hasDefault && value === undefined) {
  390. const defaultValue = opt.default
  391. if (opt.type !== Function && isFunction(defaultValue)) {
  392. const { propsDefaults } = instance
  393. if (key in propsDefaults) {
  394. value = propsDefaults[key]
  395. } else {
  396. setCurrentInstance(instance)
  397. value = propsDefaults[key] = defaultValue.call(
  398. __COMPAT__ &&
  399. isCompatEnabled(DeprecationTypes.PROPS_DEFAULT_THIS, instance)
  400. ? createPropsDefaultThis(instance, props, key)
  401. : null,
  402. props
  403. )
  404. unsetCurrentInstance()
  405. }
  406. } else {
  407. value = defaultValue
  408. }
  409. }
  410. // boolean casting
  411. if (opt[BooleanFlags.shouldCast]) {
  412. if (isAbsent && !hasDefault) {
  413. value = false
  414. } else if (
  415. opt[BooleanFlags.shouldCastTrue] &&
  416. (value === '' || value === hyphenate(key))
  417. ) {
  418. value = true
  419. }
  420. }
  421. }
  422. return value
  423. }
  424. export function normalizePropsOptions(
  425. comp: ConcreteComponent,
  426. appContext: AppContext,
  427. asMixin = false
  428. ): NormalizedPropsOptions {
  429. const cache = appContext.propsCache
  430. const cached = cache.get(comp)
  431. if (cached) {
  432. return cached
  433. }
  434. const raw = comp.props
  435. const normalized: NormalizedPropsOptions[0] = {}
  436. const needCastKeys: NormalizedPropsOptions[1] = []
  437. // apply mixin/extends props
  438. let hasExtends = false
  439. if (__FEATURE_OPTIONS_API__ && !isFunction(comp)) {
  440. const extendProps = (raw: ComponentOptions) => {
  441. if (__COMPAT__ && isFunction(raw)) {
  442. raw = raw.options
  443. }
  444. hasExtends = true
  445. const [props, keys] = normalizePropsOptions(raw, appContext, true)
  446. extend(normalized, props)
  447. if (keys) needCastKeys.push(...keys)
  448. }
  449. if (!asMixin && appContext.mixins.length) {
  450. appContext.mixins.forEach(extendProps)
  451. }
  452. if (comp.extends) {
  453. extendProps(comp.extends)
  454. }
  455. if (comp.mixins) {
  456. comp.mixins.forEach(extendProps)
  457. }
  458. }
  459. if (!raw && !hasExtends) {
  460. cache.set(comp, EMPTY_ARR as any)
  461. return EMPTY_ARR as any
  462. }
  463. if (isArray(raw)) {
  464. for (let i = 0; i < raw.length; i++) {
  465. if (__DEV__ && !isString(raw[i])) {
  466. warn(`props must be strings when using array syntax.`, raw[i])
  467. }
  468. const normalizedKey = camelize(raw[i])
  469. if (validatePropName(normalizedKey)) {
  470. normalized[normalizedKey] = EMPTY_OBJ
  471. }
  472. }
  473. } else if (raw) {
  474. if (__DEV__ && !isObject(raw)) {
  475. warn(`invalid props options`, raw)
  476. }
  477. for (const key in raw) {
  478. const normalizedKey = camelize(key)
  479. if (validatePropName(normalizedKey)) {
  480. const opt = raw[key]
  481. const prop: NormalizedProp = (normalized[normalizedKey] =
  482. isArray(opt) || isFunction(opt) ? { type: opt } : opt)
  483. if (prop) {
  484. const booleanIndex = getTypeIndex(Boolean, prop.type)
  485. const stringIndex = getTypeIndex(String, prop.type)
  486. prop[BooleanFlags.shouldCast] = booleanIndex > -1
  487. prop[BooleanFlags.shouldCastTrue] =
  488. stringIndex < 0 || booleanIndex < stringIndex
  489. // if the prop needs boolean casting or default value
  490. if (booleanIndex > -1 || hasOwn(prop, 'default')) {
  491. needCastKeys.push(normalizedKey)
  492. }
  493. }
  494. }
  495. }
  496. }
  497. const res: NormalizedPropsOptions = [normalized, needCastKeys]
  498. cache.set(comp, res)
  499. return res
  500. }
  501. function validatePropName(key: string) {
  502. if (key[0] !== '$') {
  503. return true
  504. } else if (__DEV__) {
  505. warn(`Invalid prop name: "${key}" is a reserved property.`)
  506. }
  507. return false
  508. }
  509. // use function string name to check type constructors
  510. // so that it works across vms / iframes.
  511. function getType(ctor: Prop<any>): string {
  512. const match = ctor && ctor.toString().match(/^\s*function (\w+)/)
  513. return match ? match[1] : ctor === null ? 'null' : ''
  514. }
  515. function isSameType(a: Prop<any>, b: Prop<any>): boolean {
  516. return getType(a) === getType(b)
  517. }
  518. function getTypeIndex(
  519. type: Prop<any>,
  520. expectedTypes: PropType<any> | void | null | true
  521. ): number {
  522. if (isArray(expectedTypes)) {
  523. return expectedTypes.findIndex(t => isSameType(t, type))
  524. } else if (isFunction(expectedTypes)) {
  525. return isSameType(expectedTypes, type) ? 0 : -1
  526. }
  527. return -1
  528. }
  529. /**
  530. * dev only
  531. */
  532. function validateProps(
  533. rawProps: Data,
  534. props: Data,
  535. instance: ComponentInternalInstance
  536. ) {
  537. const resolvedValues = toRaw(props)
  538. const options = instance.propsOptions[0]
  539. for (const key in options) {
  540. let opt = options[key]
  541. if (opt == null) continue
  542. validateProp(
  543. key,
  544. resolvedValues[key],
  545. opt,
  546. !hasOwn(rawProps, key) && !hasOwn(rawProps, hyphenate(key))
  547. )
  548. }
  549. }
  550. /**
  551. * dev only
  552. */
  553. function validateProp(
  554. name: string,
  555. value: unknown,
  556. prop: PropOptions,
  557. isAbsent: boolean
  558. ) {
  559. const { type, required, validator } = prop
  560. // required!
  561. if (required && isAbsent) {
  562. warn('Missing required prop: "' + name + '"')
  563. return
  564. }
  565. // missing but optional
  566. if (value == null && !prop.required) {
  567. return
  568. }
  569. // type check
  570. if (type != null && type !== true) {
  571. let isValid = false
  572. const types = isArray(type) ? type : [type]
  573. const expectedTypes = []
  574. // value is valid as long as one of the specified types match
  575. for (let i = 0; i < types.length && !isValid; i++) {
  576. const { valid, expectedType } = assertType(value, types[i])
  577. expectedTypes.push(expectedType || '')
  578. isValid = valid
  579. }
  580. if (!isValid) {
  581. warn(getInvalidTypeMessage(name, value, expectedTypes))
  582. return
  583. }
  584. }
  585. // custom validator
  586. if (validator && !validator(value)) {
  587. warn('Invalid prop: custom validator check failed for prop "' + name + '".')
  588. }
  589. }
  590. const isSimpleType = /*#__PURE__*/ makeMap(
  591. 'String,Number,Boolean,Function,Symbol,BigInt'
  592. )
  593. type AssertionResult = {
  594. valid: boolean
  595. expectedType: string
  596. }
  597. /**
  598. * dev only
  599. */
  600. function assertType(value: unknown, type: PropConstructor): AssertionResult {
  601. let valid
  602. const expectedType = getType(type)
  603. if (isSimpleType(expectedType)) {
  604. const t = typeof value
  605. valid = t === expectedType.toLowerCase()
  606. // for primitive wrapper objects
  607. if (!valid && t === 'object') {
  608. valid = value instanceof type
  609. }
  610. } else if (expectedType === 'Object') {
  611. valid = isObject(value)
  612. } else if (expectedType === 'Array') {
  613. valid = isArray(value)
  614. } else if (expectedType === 'null') {
  615. valid = value === null
  616. } else {
  617. valid = value instanceof type
  618. }
  619. return {
  620. valid,
  621. expectedType
  622. }
  623. }
  624. /**
  625. * dev only
  626. */
  627. function getInvalidTypeMessage(
  628. name: string,
  629. value: unknown,
  630. expectedTypes: string[]
  631. ): string {
  632. let message =
  633. `Invalid prop: type check failed for prop "${name}".` +
  634. ` Expected ${expectedTypes.map(capitalize).join(' | ')}`
  635. const expectedType = expectedTypes[0]
  636. const receivedType = toRawType(value)
  637. const expectedValue = styleValue(value, expectedType)
  638. const receivedValue = styleValue(value, receivedType)
  639. // check if we need to specify expected value
  640. if (
  641. expectedTypes.length === 1 &&
  642. isExplicable(expectedType) &&
  643. !isBoolean(expectedType, receivedType)
  644. ) {
  645. message += ` with value ${expectedValue}`
  646. }
  647. message += `, got ${receivedType} `
  648. // check if we need to specify received value
  649. if (isExplicable(receivedType)) {
  650. message += `with value ${receivedValue}.`
  651. }
  652. return message
  653. }
  654. /**
  655. * dev only
  656. */
  657. function styleValue(value: unknown, type: string): string {
  658. if (type === 'String') {
  659. return `"${value}"`
  660. } else if (type === 'Number') {
  661. return `${Number(value)}`
  662. } else {
  663. return `${value}`
  664. }
  665. }
  666. /**
  667. * dev only
  668. */
  669. function isExplicable(type: string): boolean {
  670. const explicitTypes = ['string', 'number', 'boolean']
  671. return explicitTypes.some(elem => type.toLowerCase() === elem)
  672. }
  673. /**
  674. * dev only
  675. */
  676. function isBoolean(...args: string[]): boolean {
  677. return args.some(elem => elem.toLowerCase() === 'boolean')
  678. }