156 lines
5.9 KiB
JavaScript
156 lines
5.9 KiB
JavaScript
var assert = require('./assert');
|
|
var isFunction = require('./isFunction');
|
|
var isNil = require('./isNil');
|
|
var getTypeName = require('./getTypeName');
|
|
var isObject = require('./isObject');
|
|
var isArray = require('./isArray');
|
|
var isType = require('./isType');
|
|
var create = require('./create');
|
|
var assign = require('./assign');
|
|
|
|
function assignMany(values) {
|
|
return values.reduce(function (acc, v) {
|
|
return assign(acc, v);
|
|
}, {});
|
|
}
|
|
|
|
function fromJSON(value, type, path) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
assert(isFunction(type), function () {
|
|
return 'Invalid argument type ' + assert.stringify(type) + ' supplied to fromJSON(value, type) (expected a type)';
|
|
});
|
|
path = path || [getTypeName(type)];
|
|
}
|
|
|
|
if (isFunction(type.fromJSON)) {
|
|
return create(type, type.fromJSON(value), path);
|
|
}
|
|
|
|
if (!isType(type)) {
|
|
return value instanceof type ? value : new type(value);
|
|
}
|
|
|
|
var kind = type.meta.kind;
|
|
var k;
|
|
var ret;
|
|
|
|
switch (kind) {
|
|
|
|
case 'maybe' :
|
|
return isNil(value) ? null : fromJSON(value, type.meta.type, path);
|
|
|
|
case 'subtype' : // the kind of a refinement is 'subtype' (for legacy reasons)
|
|
ret = fromJSON(value, type.meta.type, path);
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
assert(type.meta.predicate(ret), function () {
|
|
return 'Invalid argument value ' + assert.stringify(value) + ' supplied to fromJSON(value, type) (expected a valid ' + getTypeName(type) + ')';
|
|
});
|
|
}
|
|
return ret;
|
|
|
|
case 'struct' :
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
assert(isObject(value), function () {
|
|
return 'Invalid argument value ' + assert.stringify(value) + ' supplied to fromJSON(value, type) (expected an object for type ' + getTypeName(type) + ')';
|
|
});
|
|
}
|
|
var props = type.meta.props;
|
|
var defaultProps = type.meta.defaultProps;
|
|
ret = {};
|
|
for (k in props) {
|
|
var actual = value[k];
|
|
if (actual === undefined) {
|
|
actual = defaultProps[k];
|
|
}
|
|
if (props.hasOwnProperty(k)) {
|
|
ret[k] = fromJSON(actual, props[k], ( process.env.NODE_ENV !== 'production' ? path.concat(k + ': ' + getTypeName(props[k])) : null ));
|
|
}
|
|
}
|
|
return new type(ret);
|
|
|
|
case 'interface' :
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
assert(isObject(value), function () {
|
|
return 'Invalid argument value ' + assert.stringify(value) + ' supplied to fromJSON(value, type) (expected an object)';
|
|
});
|
|
}
|
|
var interProps = type.meta.props;
|
|
ret = {};
|
|
for (k in interProps) {
|
|
if (interProps.hasOwnProperty(k)) {
|
|
ret[k] = fromJSON(value[k], interProps[k], ( process.env.NODE_ENV !== 'production' ? path.concat(k + ': ' + getTypeName(interProps[k])) : null ));
|
|
}
|
|
}
|
|
return ret;
|
|
|
|
case 'list' :
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
assert(isArray(value), function () {
|
|
return 'Invalid argument value ' + assert.stringify(value) + ' supplied to fromJSON(value, type) (expected an array for type ' + getTypeName(type) + ')';
|
|
});
|
|
}
|
|
var elementType = type.meta.type;
|
|
var elementTypeName = getTypeName(elementType);
|
|
return value.map(function (element, i) {
|
|
return fromJSON(element, elementType, ( process.env.NODE_ENV !== 'production' ? path.concat(i + ': ' + elementTypeName) : null ));
|
|
});
|
|
|
|
case 'union' :
|
|
var actualType = type.dispatch(value);
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
assert(isFunction(actualType), function () {
|
|
return 'Invalid argument value ' + assert.stringify(value) + ' supplied to fromJSON(value, type) (no constructor returned by dispatch of union ' + getTypeName(type) + ')';
|
|
});
|
|
}
|
|
return fromJSON(value, actualType, path);
|
|
|
|
case 'tuple' :
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
assert(isArray(value), function () {
|
|
return 'Invalid argument value ' + assert.stringify(value) + ' supplied to fromJSON(value, type) (expected an array for type ' + getTypeName(type) + ')';
|
|
});
|
|
}
|
|
var types = type.meta.types;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
assert(isArray(value) && value.length === types.length, function () {
|
|
return 'Invalid value ' + assert.stringify(value) + ' supplied to fromJSON(value, type) (expected an array of length ' + types.length + ' for type ' + getTypeName(type) + ')';
|
|
});
|
|
}
|
|
return value.map(function (element, i) {
|
|
return fromJSON(element, types[i], ( process.env.NODE_ENV !== 'production' ? path.concat(i + ': ' + getTypeName(types[i])) : null ));
|
|
});
|
|
|
|
case 'dict' :
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
assert(isObject(value), function () {
|
|
return 'Invalid argument value ' + assert.stringify(value) + ' supplied to fromJSON(value, type) (expected an object for type ' + getTypeName(type) + ')';
|
|
});
|
|
}
|
|
var domain = type.meta.domain;
|
|
var codomain = type.meta.codomain;
|
|
var domainName = getTypeName(domain);
|
|
var codomainName = getTypeName(codomain);
|
|
ret = {};
|
|
for (k in value) {
|
|
if (value.hasOwnProperty(k)) {
|
|
ret[domain(k, ( process.env.NODE_ENV !== 'production' ? path.concat(domainName) : null ))] = fromJSON(value[k], codomain, ( process.env.NODE_ENV !== 'production' ? path.concat(k + ': ' + codomainName) : null ));
|
|
}
|
|
}
|
|
return ret;
|
|
|
|
case 'intersection' :
|
|
var values = type.meta.types.map(function (type, i) {
|
|
return fromJSON(value, type, ( process.env.NODE_ENV !== 'production' ? path.concat(i + ': ' + getTypeName(type)) : null ));
|
|
});
|
|
return type(
|
|
isObject(values[0]) ? assignMany(values) : value,
|
|
path
|
|
);
|
|
|
|
default : // enums, irreducible
|
|
return type(value, path);
|
|
}
|
|
}
|
|
|
|
module.exports = fromJSON;
|