Files
react/src/core/ReactProps.js
T
CommitSyncScript 54d3134da2 Add ReactProps.func
This adds ReactProps.func so people don't need to write the
slightly-more-cryptic ReactProps.instanceOf(Function). We should have
had this all along.
2013-06-06 14:29:44 -07:00

159 lines
4.7 KiB
JavaScript

/**
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @providesModule ReactProps
*/
"use strict";
var createObjectFrom = require('createObjectFrom');
var invariant = require('invariant');
/**
* Collection of methods that allow declaration and validation of props that are
* supplied to React components. Example usage:
*
* var Props = require('ReactProps');
* var MyArticle = React.createClass({
* props: {
* // An optional string prop named "description".
* description: Props.string,
*
* // A required enum prop named "category".
* category: Props.oneOf(['News','Photos']).isRequired,
*
* // A prop named "dialog" that requires an instance of Dialog.
* dialog: Props.instanceOf(Dialog).isRequired
* },
* render: function() { ... }
* });
*
* A more formal specification of how these methods are used:
*
* type := array|bool|object|number|string|oneOf([...])|instanceOf(...)
* decl := ReactProps.{type}(.isRequired)?
*
* Each and every declaration produces a function with the same signature. This
* allows the creation of custom validation functions. For example:
*
* var Props = require('ReactProps');
* var MyLink = React.createClass({
* props: {
* // An optional string or URI prop named "href".
* href: function(props, propName, componentName) {
* var propValue = props[propName];
* invariant(
* propValue == null ||
* typeof propValue === string ||
* propValue instanceof URI,
* 'Invalid `%s` supplied to `%s`, expected string or URI.',
* propName,
* componentName
* );
* }
* },
* render: function() { ... }
* });
*
* @internal
*/
var Props = {
array: createPrimitiveTypeChecker('array'),
bool: createPrimitiveTypeChecker('boolean'),
func: createPrimitiveTypeChecker('function'),
number: createPrimitiveTypeChecker('number'),
object: createPrimitiveTypeChecker('object'),
string: createPrimitiveTypeChecker('string'),
oneOf: createEnumTypeChecker,
instanceOf: createInstanceTypeChecker
};
var ANONYMOUS = '<<anonymous>>';
function createPrimitiveTypeChecker(expectedType) {
function validatePrimitiveType(propValue, propName, componentName) {
var propType = typeof propValue;
if (propType === 'object' && Array.isArray(propValue)) {
propType = 'array';
}
invariant(
propType === expectedType,
'Invalid prop `%s` of type `%s` supplied to `%s`, expected `%s`.',
propName,
propType,
componentName,
expectedType
);
}
return createChainableTypeChecker(validatePrimitiveType);
}
function createEnumTypeChecker(expectedValues) {
var expectedEnum = createObjectFrom(expectedValues);
function validateEnumType(propValue, propName, componentName) {
invariant(
expectedEnum[propValue],
'Invalid prop `%s` supplied to `%s`, expected one of %s.',
propName,
componentName,
JSON.stringify(Object.keys(expectedEnum))
);
}
return createChainableTypeChecker(validateEnumType);
}
function createInstanceTypeChecker(expectedClass) {
function validateInstanceType(propValue, propName, componentName) {
invariant(
propValue instanceof expectedClass,
'Invalid prop `%s` supplied to `%s`, expected instance of `%s`.',
propName,
componentName,
expectedClass.name || ANONYMOUS
);
}
return createChainableTypeChecker(validateInstanceType);
}
function createChainableTypeChecker(validate) {
function createTypeChecker(isRequired) {
function checkType(props, propName, componentName) {
var propValue = props[propName];
if (propValue != null) {
// Only validate if there is a value to check.
validate(propValue, propName, componentName || ANONYMOUS);
} else {
invariant(
!isRequired,
'Required prop `%s` was not specified in `%s`.',
propName,
componentName || ANONYMOUS
);
}
}
if (!isRequired) {
checkType.isRequired = createTypeChecker(true);
}
return checkType;
}
return createTypeChecker(false);
}
module.exports = Props;