props.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /* @flow */
  2. import { hasOwn, isObject, isPlainObject } from 'shared/util'
  3. import { observe, observerState } from '../observer/index'
  4. import { warn } from './debug'
  5. type PropOptions = {
  6. type: Function | Array<Function> | null,
  7. default: any,
  8. required: ?boolean,
  9. validator: ?Function
  10. }
  11. export function validateProp (vm: Component, key: string, propsData: ?Object): any {
  12. if (!vm.$options.props || !propsData) return
  13. const prop = vm.$options.props[key]
  14. const absent = hasOwn(propsData, key)
  15. let value = propsData[key]
  16. // check default value
  17. if (value === undefined) {
  18. value = getPropDefaultValue(vm, prop, key)
  19. // since the default value is a fresh copy,
  20. // make sure to observe it.
  21. observerState.shouldConvert = true
  22. observe(value)
  23. observerState.shouldConvert = false
  24. }
  25. if (process.env.NODE_ENV !== 'production') {
  26. assertProp(prop, key, value, vm, absent)
  27. }
  28. return value
  29. }
  30. /**
  31. * Get the default value of a prop.
  32. */
  33. function getPropDefaultValue (vm: Component, prop: PropOptions, name: string): any {
  34. // no default, return undefined
  35. if (!hasOwn(prop, 'default')) {
  36. // absent boolean value defaults to false
  37. return prop.type === Boolean
  38. ? false
  39. : undefined
  40. }
  41. const def = prop.default
  42. // warn against non-factory defaults for Object & Array
  43. if (isObject(def)) {
  44. process.env.NODE_ENV !== 'production' && warn(
  45. 'Invalid default value for prop "' + name + '": ' +
  46. 'Props with type Object/Array must use a factory function ' +
  47. 'to return the default value.',
  48. vm
  49. )
  50. }
  51. // call factory function for non-Function types
  52. return typeof def === 'function' && prop.type !== Function
  53. ? def.call(vm)
  54. : def
  55. }
  56. /**
  57. * Assert whether a prop is valid.
  58. */
  59. function assertProp (
  60. prop: PropOptions,
  61. name: string,
  62. value: any,
  63. vm: Component,
  64. absent: boolean
  65. ) {
  66. if (prop.required && absent) {
  67. warn(
  68. 'Missing required prop: "' + name + '"',
  69. vm
  70. )
  71. return
  72. }
  73. if (value == null) {
  74. return
  75. }
  76. let type = prop.type
  77. let valid = !type
  78. const expectedTypes = []
  79. if (type) {
  80. if (!Array.isArray(type)) {
  81. type = [type]
  82. }
  83. for (let i = 0; i < type.length && !valid; i++) {
  84. const assertedType = assertType(value, type[i])
  85. expectedTypes.push(assertedType.expectedType)
  86. valid = assertedType.valid
  87. }
  88. }
  89. if (!valid) {
  90. warn(
  91. 'Invalid prop: type check failed for prop "' + name + '".' +
  92. ' Expected ' + expectedTypes.join(', ') +
  93. ', got ' + Object.prototype.toString.call(value).slice(8, -1) + '.',
  94. vm
  95. )
  96. return
  97. }
  98. const validator = prop.validator
  99. if (validator) {
  100. if (!validator(value)) {
  101. warn(
  102. 'Invalid prop: custom validator check failed for prop "' + name + '".',
  103. vm
  104. )
  105. }
  106. }
  107. }
  108. /**
  109. * Assert the type of a value
  110. */
  111. function assertType (value: any, type: Function): {
  112. valid: boolean,
  113. expectedType: string
  114. } {
  115. let valid
  116. let expectedType
  117. if (type === String) {
  118. expectedType = 'string'
  119. valid = typeof value === expectedType
  120. } else if (type === Number) {
  121. expectedType = 'number'
  122. valid = typeof value === expectedType
  123. } else if (type === Boolean) {
  124. expectedType = 'boolean'
  125. valid = typeof value === expectedType
  126. } else if (type === Function) {
  127. expectedType = 'function'
  128. valid = typeof value === expectedType
  129. } else if (type === Object) {
  130. expectedType = 'Object'
  131. valid = isPlainObject(value)
  132. } else if (type === Array) {
  133. expectedType = 'Array'
  134. valid = Array.isArray(value)
  135. } else {
  136. expectedType = type.name || type.toString()
  137. valid = value instanceof type
  138. }
  139. return {
  140. valid,
  141. expectedType
  142. }
  143. }