2020-06-13 04:59:15 +08:00
import {
toRaw ,
shallowReactive ,
trigger ,
TriggerOpTypes
} 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 ,
2020-02-11 02:15:36 +08:00
makeMap ,
2020-03-22 04:25:33 +08:00
isReservedProp ,
2020-04-07 05:57:27 +08:00
EMPTY_ARR ,
2020-06-11 04:54:23 +08:00
def ,
extend
2019-05-28 18:06:00 +08:00
} from '@vue/shared'
import { warn } from './warning'
2020-06-09 23:27:40 +08:00
import {
Data ,
ComponentInternalInstance ,
ComponentOptions ,
2020-09-18 03:59:01 +08:00
ConcreteComponent ,
setCurrentInstance
2020-06-09 23:27:40 +08:00
} from './component'
2020-04-04 08:40:34 +08:00
import { isEmitListener } from './componentEmits'
2020-04-16 04:45:20 +08:00
import { InternalObjectKey } from './vnode'
2020-09-01 06:32:07 +08:00
import { AppContext } from './apiCreateApp'
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
}
2020-08-18 23:23:18 +08:00
export type Prop < T , D = T > = PropOptions < T , D > | PropType < T >
2019-05-28 18:06:00 +08:00
2020-08-18 23:37:34 +08:00
type DefaultFactory < T > = ( props : Data ) = > T | null | undefined
2019-10-30 23:11:23 +08:00
2020-08-18 23:23:18 +08:00
interface PropOptions < T = any , D = T > {
2019-05-28 18:06:00 +08:00
type ? : PropType < T > | true | null
required? : boolean
2020-08-20 04:36:42 +08:00
default ? : D | DefaultFactory < D > | null | undefined | object
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 > [ ]
2020-04-03 21:28:13 +08:00
type PropConstructor < T = any > =
2021-02-25 05:20:41 +08:00
| { new ( . . . args : any [ ] ) : T & { } }
2020-04-03 21:28:13 +08:00
| { ( ) : T }
| PropMethod < T >
2021-03-25 23:27:54 +08:00
type PropMethod < T , TConstructor = any > = [ T ] extends [ ( . . . args : any ) = > any ] // if is function with args
2020-06-12 05:34:21 +08:00
? { new ( ) : TConstructor ; ( ) : T ; readonly prototype : TConstructor } // Create Function like constructor
2020-04-03 21:28:13 +08:00
: never
2019-06-01 00:47:05 +08:00
2020-09-16 22:09:35 +08:00
type RequiredKeys < T > = {
2020-10-20 05:25:55 +08:00
[ K in keyof T ] : T [ K ] extends
| { required : true }
| { default : any }
// don't mark Boolean props as undefined
| BooleanConstructor
| { type : BooleanConstructor }
? K
: never
2019-06-01 00:47:05 +08:00
} [ keyof T ]
2020-09-16 22:09:35 +08:00
type OptionalKeys < T > = Exclude < keyof T , RequiredKeys < T > >
type DefaultKeys < T > = {
2020-10-20 05:25:55 +08:00
[ 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
2020-09-16 22:09:35 +08:00
} [ keyof T ]
2019-06-01 00:47:05 +08:00
2021-03-25 23:27:54 +08:00
type InferPropType < T > = [ T ] extends [ null ]
2019-06-01 17:43:41 +08:00
? any // null & true would fail to infer
2021-03-25 23:27:54 +08:00
: [ T ] extends [ { type : null | true } ]
2020-06-09 22:37:00 +08:00
? 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`
2021-03-25 23:27:54 +08:00
: [ T ] extends [ ObjectConstructor | { type : ObjectConstructor } ]
2020-07-19 10:56:28 +08:00
? Record < string , any >
2021-03-25 23:27:54 +08:00
: [ T ] extends [ BooleanConstructor | { type : BooleanConstructor } ]
2020-06-09 22:37:00 +08:00
? boolean
2021-03-25 23:27:54 +08:00
: [ T ] extends [ DateConstructor | { type : DateConstructor } ]
2021-02-06 02:56:23 +08:00
? Date
2021-03-25 23:27:54 +08:00
: [ T ] extends [ Prop < infer V , infer D > ]
? ( unknown extends V ? D : V )
: T
2019-06-01 00:47:05 +08:00
2020-09-16 22:09:35 +08:00
export type ExtractPropTypes < O > = O extends object
? { [ K in RequiredKeys < O > ] : InferPropType < O [ K ] > } &
{ [ K in OptionalKeys < O > ] ? : 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
}
2020-09-16 22:09:35 +08:00
// extract props which defined with default from prop options
export type ExtractDefaultPropTypes < O > = O extends object
? { [ K in DefaultKeys < O > ] : InferPropType < O [ K ] > }
: { }
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)
2020-09-01 06:32:07 +08:00
export type NormalizedProps = Record < string , NormalizedProp >
export type NormalizedPropsOptions = [ NormalizedProps , string [ ] ] | [ ]
2019-05-28 18:06:00 +08:00
2020-04-07 05:37:47 +08:00
export function initProps (
2019-09-07 00:58:31 +08:00
instance : ComponentInternalInstance ,
2020-04-07 05:37:47 +08:00
rawProps : Data | null ,
isStateful : number , // result of bitwise flag comparison
isSSR = false
2019-05-29 11:36:16 +08:00
) {
2020-04-07 05:37:47 +08:00
const props : Data = { }
const attrs : Data = { }
2020-04-16 04:45:20 +08:00
def ( attrs , InternalObjectKey , 1 )
2021-03-26 05:26:58 +08:00
instance . propsDefaults = Object . create ( null )
2020-04-07 05:37:47 +08:00
setFullProps ( instance , rawProps , props , attrs )
// validation
2020-06-09 23:27:40 +08:00
if ( __DEV__ ) {
2021-03-26 04:22:43 +08:00
validateProps ( rawProps || { } , props , instance )
2019-05-28 18:06:00 +08:00
}
2019-05-30 23:16:15 +08:00
2020-04-07 05:37:47 +08:00
if ( isStateful ) {
// stateful
2020-04-15 11:49:46 +08:00
instance . props = isSSR ? props : shallowReactive ( props )
2020-04-07 05:37:47 +08:00
} else {
2020-06-09 23:27:40 +08:00
if ( ! instance . type . props ) {
2020-04-07 05:37:47 +08:00
// functional w/ optional props, props === attrs
instance . props = attrs
} else {
// functional w/ declared props
instance . props = props
}
}
instance . attrs = attrs
}
2019-05-30 23:16:15 +08:00
2020-04-07 05:37:47 +08:00
export function updateProps (
instance : ComponentInternalInstance ,
rawProps : Data | null ,
2020-04-21 02:16:25 +08:00
rawPrevProps : Data | null ,
2020-04-07 05:37:47 +08:00
optimized : boolean
) {
const {
props ,
attrs ,
vnode : { patchFlag }
} = instance
const rawCurrentProps = toRaw ( props )
2020-09-01 06:32:07 +08:00
const [ options ] = instance . propsOptions
2020-04-07 05:37:47 +08:00
2020-08-25 06:13:17 +08:00
if (
2020-10-15 23:05:12 +08:00
// always force full diff in dev
// - #1942 if hmr is enabled with sfc component
// - vite#872 non-sfc component used by sfc component
! (
__DEV__ &&
( instance . type . __hmrId ||
( instance . parent && instance . parent . type . __hmrId ) )
) &&
2020-08-25 06:13:17 +08:00
( optimized || patchFlag > 0 ) &&
! ( patchFlag & PatchFlags . FULL_PROPS )
) {
2020-04-07 05:37:47 +08:00
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 ++ ) {
const key = propsToUpdate [ i ]
// 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 ) ) {
attrs [ key ] = value
} else {
const camelizedKey = camelize ( key )
props [ camelizedKey ] = resolvePropValue (
options ,
rawCurrentProps ,
camelizedKey ,
2020-09-18 03:59:01 +08:00
value ,
instance
2020-04-07 05:37:47 +08:00
)
}
} else {
attrs [ key ] = value
}
}
}
} else {
// full props update.
setFullProps ( instance , rawProps , props , attrs )
// in case of dynamic props, check if we need to delete keys from
// the props object
2020-04-14 00:37:31 +08:00
let kebabKey : string
2020-04-07 05:37:47 +08:00
for ( const key in rawCurrentProps ) {
2020-04-14 00:37:31 +08:00
if (
! rawProps ||
2020-07-07 04:58:46 +08:00
// for camelCase
( ! hasOwn ( rawProps , key ) &&
2020-04-14 00:37:31 +08:00
// it's possible the original props was passed in as kebab-case
// and converted to camelCase (#955)
( ( kebabKey = hyphenate ( key ) ) === key || ! hasOwn ( rawProps , kebabKey ) ) )
) {
2020-04-15 04:17:35 +08:00
if ( options ) {
2020-07-07 04:58:46 +08:00
if (
rawPrevProps &&
2020-06-26 21:16:06 +08:00
// for camelCase
2020-07-07 04:58:46 +08:00
( rawPrevProps [ key ] !== undefined ||
// for kebab-case
rawPrevProps [ kebabKey ! ] !== undefined )
) {
2020-04-21 02:16:25 +08:00
props [ key ] = resolvePropValue (
options ,
rawProps || EMPTY_OBJ ,
key ,
2020-09-18 03:59:01 +08:00
undefined ,
instance
2020-04-21 02:16:25 +08:00
)
}
2020-04-15 04:17:35 +08:00
} else {
delete props [ key ]
}
2020-04-07 05:37:47 +08:00
}
}
2020-04-21 02:16:25 +08:00
// 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 ) ) {
delete attrs [ key ]
}
2020-04-07 05:37:47 +08:00
}
}
}
2020-06-13 04:59:15 +08:00
// trigger updates for $attrs in case it's used in component slots
trigger ( instance , TriggerOpTypes . SET , '$attrs' )
2021-03-26 04:22:43 +08:00
if ( __DEV__ ) {
validateProps ( rawProps || { } , props , instance )
2020-04-07 05:37:47 +08:00
}
}
function setFullProps (
instance : ComponentInternalInstance ,
rawProps : Data | null ,
props : Data ,
attrs : Data
) {
2020-09-01 06:32:07 +08:00
const [ options , needCastKeys ] = instance . propsOptions
2020-03-19 06:14:51 +08:00
if ( rawProps ) {
2019-05-29 09:18:45 +08:00
for ( const key in rawProps ) {
2020-02-11 02:15:36 +08:00
const value = rawProps [ key ]
2019-11-21 10:56:17 +08:00
// key, ref are reserved and never passed down
2020-02-11 02:15:36 +08:00
if ( isReservedProp ( key ) ) {
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-04-04 00:05:52 +08:00
let camelKey
2020-04-07 05:37:47 +08:00
if ( options && hasOwn ( options , ( camelKey = camelize ( key ) ) ) ) {
props [ camelKey ] = value
2020-09-01 06:32:07 +08:00
} else if ( ! isEmitListener ( instance . emitsOptions , key ) ) {
2020-04-04 00:05:52 +08:00
// 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
2020-04-07 05:37:47 +08:00
attrs [ key ] = value
2019-05-28 18:06:00 +08:00
}
}
}
2020-04-04 00:05:52 +08:00
2020-04-07 05:37:47 +08:00
if ( needCastKeys ) {
2020-04-21 02:16:25 +08:00
const rawCurrentProps = toRaw ( props )
2019-12-13 11:07:48 +08:00
for ( let i = 0 ; i < needCastKeys . length ; i ++ ) {
const key = needCastKeys [ i ]
2020-04-21 02:16:25 +08:00
props [ key ] = resolvePropValue (
options ! ,
rawCurrentProps ,
key ,
2020-09-18 03:59:01 +08:00
rawCurrentProps [ key ] ,
instance
2020-04-21 02:16:25 +08:00
)
2019-05-28 18:06:00 +08:00
}
}
2020-04-07 05:37:47 +08:00
}
2019-05-29 11:36:16 +08:00
2020-04-07 05:37:47 +08:00
function resolvePropValue (
2020-09-01 06:32:07 +08:00
options : NormalizedProps ,
2020-04-07 05:37:47 +08:00
props : Data ,
key : string ,
2020-09-18 03:59:01 +08:00
value : unknown ,
instance : ComponentInternalInstance
2020-04-07 05:37:47 +08:00
) {
2020-06-25 23:05:39 +08:00
const opt = options [ key ]
2020-04-15 04:17:35 +08:00
if ( opt != null ) {
const hasDefault = hasOwn ( opt , 'default' )
// default values
if ( hasDefault && value === undefined ) {
const defaultValue = opt . default
2020-09-18 03:59:01 +08:00
if ( opt . type !== Function && isFunction ( defaultValue ) ) {
2021-03-26 05:26:58 +08:00
const { propsDefaults } = instance
if ( key in propsDefaults ) {
value = propsDefaults [ key ]
} else {
setCurrentInstance ( instance )
value = propsDefaults [ key ] = defaultValue ( props )
setCurrentInstance ( null )
}
2020-09-18 03:59:01 +08:00
} else {
value = defaultValue
}
2020-04-15 04:17:35 +08:00
}
// boolean casting
if ( opt [ BooleanFlags . shouldCast ] ) {
if ( ! hasOwn ( props , key ) && ! hasDefault ) {
value = false
} else if (
opt [ BooleanFlags . shouldCastTrue ] &&
( value === '' || value === hyphenate ( key ) )
) {
value = true
}
2020-04-07 05:37:47 +08:00
}
2020-03-17 06:45:08 +08:00
}
2020-04-07 05:37:47 +08:00
return value
2020-03-17 06:45:08 +08:00
}
2020-03-22 04:25:33 +08:00
export function normalizePropsOptions (
2020-09-01 06:32:07 +08:00
comp : ConcreteComponent ,
appContext : AppContext ,
asMixin = false
) : NormalizedPropsOptions {
2020-10-07 03:31:29 +08:00
if ( ! appContext . deopt && comp . __props ) {
return comp . __props
2019-05-28 18:06:00 +08:00
}
2020-06-09 23:27:40 +08:00
const raw = comp . props
2020-04-04 00:05:52 +08:00
const normalized : NormalizedPropsOptions [ 0 ] = { }
2019-12-13 11:07:48 +08:00
const needCastKeys : NormalizedPropsOptions [ 1 ] = [ ]
2020-06-09 23:27:40 +08:00
// apply mixin/extends props
let hasExtends = false
2020-07-21 09:51:30 +08:00
if ( __FEATURE_OPTIONS_API__ && ! isFunction ( comp ) ) {
2020-06-09 23:27:40 +08:00
const extendProps = ( raw : ComponentOptions ) = > {
2020-09-01 06:32:07 +08:00
hasExtends = true
const [ props , keys ] = normalizePropsOptions ( raw , appContext , true )
2020-06-11 04:54:23 +08:00
extend ( normalized , props )
2020-06-09 23:27:40 +08:00
if ( keys ) needCastKeys . push ( . . . keys )
}
2020-09-01 06:32:07 +08:00
if ( ! asMixin && appContext . mixins . length ) {
appContext . mixins . forEach ( extendProps )
}
2020-06-09 23:27:40 +08:00
if ( comp . extends ) {
extendProps ( comp . extends )
}
if ( comp . mixins ) {
comp . mixins . forEach ( extendProps )
}
}
if ( ! raw && ! hasExtends ) {
2020-10-20 06:08:54 +08:00
return ( comp . __props = EMPTY_ARR as any )
2020-06-09 23:27:40 +08:00
}
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 ] )
2020-03-17 06:45:08 +08:00
if ( validatePropName ( normalizedKey ) ) {
2020-04-04 00:05:52 +08:00
normalized [ normalizedKey ] = EMPTY_OBJ
2019-05-28 18:06:00 +08:00
}
}
2020-06-09 23:27:40 +08:00
} else if ( raw ) {
2019-05-28 18:06:00 +08:00
if ( __DEV__ && ! isObject ( raw ) ) {
warn ( ` invalid props options ` , raw )
}
for ( const key in raw ) {
const normalizedKey = camelize ( key )
2020-03-17 06:45:08 +08:00
if ( validatePropName ( normalizedKey ) ) {
2019-05-28 18:06:00 +08:00
const opt = raw [ key ]
2020-04-04 00:05:52 +08:00
const prop : NormalizedProp = ( normalized [ normalizedKey ] =
2019-05-28 18:06:00 +08:00
isArray ( opt ) || isFunction ( opt ) ? { type : opt } : opt )
2020-03-19 06:14:51 +08:00
if ( prop ) {
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
2020-03-16 22:19:06 +08:00
prop [ BooleanFlags . shouldCastTrue ] =
stringIndex < 0 || 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
}
}
}
}
2020-09-01 06:32:07 +08:00
2020-10-07 03:31:29 +08:00
return ( comp . __props = [ normalized , needCastKeys ] )
}
function validatePropName ( key : string ) {
if ( key [ 0 ] !== '$' ) {
return true
} else if ( __DEV__ ) {
warn ( ` Invalid prop name: " ${ key } " is a reserved property. ` )
}
return false
2020-04-04 00:05:52 +08:00
}
2019-05-28 18:06:00 +08:00
// 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
}
}
2020-03-16 22:19:06 +08:00
} else if ( isFunction ( expectedTypes ) ) {
2019-05-28 18:06:00 +08:00
return isSameType ( expectedTypes , type ) ? 0 : - 1
}
return - 1
}
2020-06-09 23:27:40 +08:00
/ * *
* dev only
* /
2021-03-26 04:22:43 +08:00
function validateProps (
rawProps : Data ,
props : Data ,
instance : ComponentInternalInstance
) {
const resolvedValues = toRaw ( props )
2020-09-01 06:32:07 +08:00
const options = instance . propsOptions [ 0 ]
2020-04-07 05:37:47 +08:00
for ( const key in options ) {
let opt = options [ key ]
if ( opt == null ) continue
2021-03-26 04:22:43 +08:00
validateProp ( key , resolvedValues [ key ] , opt , ! hasOwn ( rawProps , key ) )
2020-04-07 05:37:47 +08:00
}
}
2020-06-09 23:27:40 +08:00
/ * *
* dev only
* /
2019-05-28 18:06:00 +08:00
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 (
2021-02-04 02:25:45 +08:00
'String,Number,Boolean,Function,Symbol,BigInt'
2019-10-23 22:34:58 +08:00
)
2019-05-28 18:06:00 +08:00
2020-04-07 05:37:47 +08:00
type AssertionResult = {
valid : boolean
expectedType : string
}
2020-06-09 23:27:40 +08:00
/ * *
* dev only
* /
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' ) {
2020-08-17 23:25:27 +08:00
valid = isObject ( value )
2019-05-28 18:06:00 +08:00
} else if ( expectedType === 'Array' ) {
valid = isArray ( value )
} else {
valid = value instanceof type
}
return {
valid ,
expectedType
}
}
2020-06-09 23:27:40 +08:00
/ * *
* dev only
* /
2019-05-28 18:06:00 +08:00
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
}
2020-06-09 23:27:40 +08:00
/ * *
* dev only
* /
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 } `
}
}
2020-06-09 23:27:40 +08:00
/ * *
* dev only
* /
2019-05-28 18:06:00 +08:00
function isExplicable ( type : string ) : boolean {
const explicitTypes = [ 'string' , 'number' , 'boolean' ]
return explicitTypes . some ( elem = > type . toLowerCase ( ) === elem )
}
2020-06-09 23:27:40 +08:00
/ * *
* dev only
* /
2019-05-28 18:06:00 +08:00
function isBoolean ( . . . args : string [ ] ) : boolean {
return args . some ( elem = > elem . toLowerCase ( ) === 'boolean' )
}