src/core/util/props.ts TYPESCRIPT 255 lines View on github.com → Search inside
1import { warn } from './debug'2import { observe, toggleObserving, shouldObserve } from '../observer/index'3import {4  hasOwn,5  isArray,6  isObject,7  isFunction,8  toRawType,9  hyphenate,10  capitalize,11  isPlainObject12} from 'shared/util'13import type { Component } from 'types/component'1415type PropOptions = {16  type: Function | Array<Function> | null17  default: any18  required?: boolean19  validator?: Function20}2122export function validateProp(23  key: string,24  propOptions: Object,25  propsData: Object,26  vm?: Component27): any {28  const prop = propOptions[key]29  const absent = !hasOwn(propsData, key)30  let value = propsData[key]31  // boolean casting32  const booleanIndex = getTypeIndex(Boolean, prop.type)33  if (booleanIndex > -1) {34    if (absent && !hasOwn(prop, 'default')) {35      value = false36    } else if (value === '' || value === hyphenate(key)) {37      // only cast empty string / same name to boolean if38      // boolean has higher priority39      const stringIndex = getTypeIndex(String, prop.type)40      if (stringIndex < 0 || booleanIndex < stringIndex) {41        value = true42      }43    }44  }45  // check default value46  if (value === undefined) {47    value = getPropDefaultValue(vm, prop, key)48    // since the default value is a fresh copy,49    // make sure to observe it.50    const prevShouldObserve = shouldObserve51    toggleObserving(true)52    observe(value)53    toggleObserving(prevShouldObserve)54  }55  if (__DEV__) {56    assertProp(prop, key, value, vm, absent)57  }58  return value59}6061/**62 * Get the default value of a prop.63 */64function getPropDefaultValue(65  vm: Component | undefined,66  prop: PropOptions,67  key: string68): any {69  // no default, return undefined70  if (!hasOwn(prop, 'default')) {71    return undefined72  }73  const def = prop.default74  // warn against non-factory defaults for Object & Array75  if (__DEV__ && isObject(def)) {76    warn(77      'Invalid default value for prop "' +78        key +79        '": ' +80        'Props with type Object/Array must use a factory function ' +81        'to return the default value.',82      vm83    )84  }85  // the raw prop value was also undefined from previous render,86  // return previous default value to avoid unnecessary watcher trigger87  if (88    vm &&89    vm.$options.propsData &&90    vm.$options.propsData[key] === undefined &&91    vm._props[key] !== undefined92  ) {93    return vm._props[key]94  }95  // call factory function for non-Function types96  // a value is Function if its prototype is function even across different execution context97  return isFunction(def) && getType(prop.type) !== 'Function'98    ? def.call(vm)99    : def100}101102/**103 * Assert whether a prop is valid.104 */105function assertProp(106  prop: PropOptions,107  name: string,108  value: any,109  vm?: Component,110  absent?: boolean111) {112  if (prop.required && absent) {113    warn('Missing required prop: "' + name + '"', vm)114    return115  }116  if (value == null && !prop.required) {117    return118  }119  let type = prop.type120  let valid = !type || (type as any) === true121  const expectedTypes: string[] = []122  if (type) {123    if (!isArray(type)) {124      type = [type]125    }126    for (let i = 0; i < type.length && !valid; i++) {127      const assertedType = assertType(value, type[i], vm)128      expectedTypes.push(assertedType.expectedType || '')129      valid = assertedType.valid130    }131  }132133  const haveExpectedTypes = expectedTypes.some(t => t)134  if (!valid && haveExpectedTypes) {135    warn(getInvalidTypeMessage(name, value, expectedTypes), vm)136    return137  }138  const validator = prop.validator139  if (validator) {140    if (!validator(value)) {141      warn(142        'Invalid prop: custom validator check failed for prop "' + name + '".',143        vm144      )145    }146  }147}148149const simpleCheckRE = /^(String|Number|Boolean|Function|Symbol|BigInt)$/150151function assertType(152  value: any,153  type: Function,154  vm?: Component155): {156  valid: boolean157  expectedType: string158} {159  let valid160  const expectedType = getType(type)161  if (simpleCheckRE.test(expectedType)) {162    const t = typeof value163    valid = t === expectedType.toLowerCase()164    // for primitive wrapper objects165    if (!valid && t === 'object') {166      valid = value instanceof type167    }168  } else if (expectedType === 'Object') {169    valid = isPlainObject(value)170  } else if (expectedType === 'Array') {171    valid = isArray(value)172  } else {173    try {174      valid = value instanceof type175    } catch (e: any) {176      warn('Invalid prop type: "' + String(type) + '" is not a constructor', vm)177      valid = false178    }179  }180  return {181    valid,182    expectedType183  }184}185186const functionTypeCheckRE = /^\s*function (\w+)/187188/**189 * Use function string name to check built-in types,190 * because a simple equality check will fail when running191 * across different vms / iframes.192 */193function getType(fn) {194  const match = fn && fn.toString().match(functionTypeCheckRE)195  return match ? match[1] : ''196}197198function isSameType(a, b) {199  return getType(a) === getType(b)200}201202function getTypeIndex(type, expectedTypes): number {203  if (!isArray(expectedTypes)) {204    return isSameType(expectedTypes, type) ? 0 : -1205  }206  for (let i = 0, len = expectedTypes.length; i < len; i++) {207    if (isSameType(expectedTypes[i], type)) {208      return i209    }210  }211  return -1212}213214function getInvalidTypeMessage(name, value, expectedTypes) {215  let message =216    `Invalid prop: type check failed for prop "${name}".` +217    ` Expected ${expectedTypes.map(capitalize).join(', ')}`218  const expectedType = expectedTypes[0]219  const receivedType = toRawType(value)220  // check if we need to specify expected value221  if (222    expectedTypes.length === 1 &&223    isExplicable(expectedType) &&224    isExplicable(typeof value) &&225    !isBoolean(expectedType, receivedType)226  ) {227    message += ` with value ${styleValue(value, expectedType)}`228  }229  message += `, got ${receivedType} `230  // check if we need to specify received value231  if (isExplicable(receivedType)) {232    message += `with value ${styleValue(value, receivedType)}.`233  }234  return message235}236237function styleValue(value, type) {238  if (type === 'String') {239    return `"${value}"`240  } else if (type === 'Number') {241    return `${Number(value)}`242  } else {243    return `${value}`244  }245}246247const EXPLICABLE_TYPES = ['string', 'number', 'boolean']248function isExplicable(value) {249  return EXPLICABLE_TYPES.some(elem => value.toLowerCase() === elem)250}251252function isBoolean(...args) {253  return args.some(elem => elem.toLowerCase() === 'boolean')254}

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.