diff --git a/src/core/util/props.js b/src/core/util/props.js index 5a90f21b5f0..52d08c173da 100644 --- a/src/core/util/props.js +++ b/src/core/util/props.js @@ -149,12 +149,12 @@ function assertProp ( const simpleCheckRE = /^(String|Number|Boolean|Function|Symbol|BigInt)$/ -function assertType (value: any, type: Function, vm: ?Component): { +function assertType (value: any, type: Function | null, vm: ?Component): { valid: boolean; expectedType: string; } { let valid - const expectedType = getType(type) + const expectedType = type ? getType(type) : 'null' if (simpleCheckRE.test(expectedType)) { const t = typeof value valid = t === expectedType.toLowerCase() @@ -162,6 +162,8 @@ function assertType (value: any, type: Function, vm: ?Component): { if (!valid && t === 'object') { valid = value instanceof type } + } else if (expectedType === 'null') { + valid = value === null } else if (expectedType === 'Object') { valid = isPlainObject(value) } else if (expectedType === 'Array') { @@ -210,7 +212,7 @@ function getTypeIndex (type, expectedTypes): number { function getInvalidTypeMessage (name, value, expectedTypes) { let message = `Invalid prop: type check failed for prop "${name}".` + - ` Expected ${expectedTypes.map(capitalize).join(', ')}` + ` Expected ${expectedTypes.map(type => type === 'null' ? type : capitalize(type)).join(', ')}` const expectedType = expectedTypes[0] const receivedType = toRawType(value) // check if we need to specify expected value diff --git a/test/unit/features/options/props.spec.js b/test/unit/features/options/props.spec.js index 2ada45a0553..aee9969f415 100644 --- a/test/unit/features/options/props.spec.js +++ b/test/unit/features/options/props.spec.js @@ -331,6 +331,51 @@ describe('Options props', () => { makeInstance(null, true) expect(console.error.calls.count()).toBe(0) }) + + it('required + null accepts anything', () => { + makeInstance({}, null, null, true) + makeInstance(null, null, null, true) + makeInstance(1, null, null, true) + makeInstance('foo', null, null, true) + makeInstance(() => {}, null, null, true) + makeInstance(undefined, null, null, true) + expect(console.error.calls.count()).toBe(0) + }) + + it('required + nullable object', () => { + makeInstance(null, [Object, null], null, true) + expect(console.error.calls.count()).toBe(0) + makeInstance({}, [Object, null], null, true) + expect(console.error.calls.count()).toBe(0) + makeInstance(undefined, [Object, null], null, true) + expect('Expected Object, null').toHaveBeenWarned() + }) + + it('non required + nullable object', () => { + makeInstance(null, [Object, null]) + expect(console.error.calls.count()).toBe(0) + makeInstance({}, [Object, null]) + expect(console.error.calls.count()).toBe(0) + makeInstance(undefined, [Object, null]) + expect(console.error.calls.count()).toBe(0) + makeInstance('hello', [Object, null]) + expect('Expected Object, null').toHaveBeenWarned() + }) + + it('required + nullable non object', () => { + makeInstance(null, [String, null], null, true) + expect(console.error.calls.count()).toBe(0) + makeInstance('foo', [String, null], null, true) + expect(console.error.calls.count()).toBe(0) + makeInstance(undefined, [String, null], null, true) + expect('Expected String, null').toHaveBeenWarned() + }) + + it('non-required nullable string fails with object', () => { + makeInstance({}, [String, null]) + expect('Expected String, null').toHaveBeenWarned() + }) + }) it('should work with v-bind', () => {