var assert = require('./assert'); var isTypeName = require('./isTypeName'); var isFunction = require('./isFunction'); var getTypeName = require('./getTypeName'); var isIdentity = require('./isIdentity'); var isArray = require('./isArray'); var create = require('./create'); var is = require('./is'); var forbidNewOperator = require('./forbidNewOperator'); var isUnion = require('./isUnion'); var isNil = require('./isNil'); function getDefaultName(types) { return types.map(getTypeName).join(' | '); } function union(types, name) { if (process.env.NODE_ENV !== 'production') { assert(isArray(types) && types.every(isFunction) && types.length >= 2, function () { return 'Invalid argument types ' + assert.stringify(types) + ' supplied to union(types, [name]) combinator (expected an array of at least 2 types)'; }); assert(isTypeName(name), function () { return 'Invalid argument name ' + assert.stringify(name) + ' supplied to union(types, [name]) combinator (expected a string)'; }); } var displayName = name || getDefaultName(types); var identity = types.every(isIdentity); function Union(value, path) { if (process.env.NODE_ENV === 'production') { if (identity) { return value; } } var type = Union.dispatch(value); if (!type && Union.is(value)) { return value; } if (process.env.NODE_ENV !== 'production') { if (identity) { forbidNewOperator(this, Union); } path = path || [displayName]; assert(isFunction(type), function () { return 'Invalid value ' + assert.stringify(value) + ' supplied to ' + path.join('/') + ' (no constructor returned by dispatch)'; }); path[path.length - 1] += '(' + getTypeName(type) + ')'; } return create(type, value, path); } Union.meta = { kind: 'union', types: types, name: name, identity: identity }; Union.displayName = displayName; Union.is = function (x) { return types.some(function (type) { return is(x, type); }); }; Union.dispatch = function (x) { // default dispatch implementation for (var i = 0, len = types.length; i < len; i++ ) { var type = types[i]; if (isUnion(type)) { // handle union of unions var t = type.dispatch(x); if (!isNil(t)) { return t; } } else if (is(x, type)) { return type; } } }; Union.update = function (instance, patch) { return Union(assert.update(instance, patch)); }; return Union; } union.getDefaultName = getDefaultName; module.exports = union;