// @flow type ExtractReturnType = (() => V) => V; type ExtractReturnOptionalType = (() => V) => ?V; export const schema = { default( fn: mixed => ?T, defaultValue: T | (() => T) ): mixed => T { const newFn = (i: mixed): T => { if (typeof defaultValue === 'function') { return fn(i) || defaultValue(); } return fn(i) || defaultValue; }; newFn._originalFn = fn; newFn.defaultValue = defaultValue; return newFn; }, obj( s: O ): mixed => $ObjMap { const newFn = i => { return normalize(i, s); }; newFn._schema = s; return newFn; }, array( fn: mixed => ?T, defaultValue: T[] | (() => T[]) = [] ): mixed => T[] { return (i: mixed) => { const arr: T[] = []; if (Array.isArray(i)) { i.forEach(c => { if (typeof c !== 'undefined' && c !== null) { const val = fn(c); if (typeof val !== 'undefined' && val !== null) { arr.push(val); } } }); } else { if (typeof i !== 'undefined' && i !== null) { const val = fn(i); if (typeof val !== 'undefined' && val !== null) { arr.push(val); } } } if (Array.isArray(defaultValue) && arr.length === 0) { return defaultValue; } return arr; }; }, number(i: mixed): ?number { if (typeof i !== 'undefined' && i !== null && i !== '' && !isNaN(+i)) { return +i; } }, bool(i: mixed): ?boolean { if (typeof i !== 'undefined') { if (typeof i === 'boolean') { return i; } else if (typeof i === 'string') { if (i === 'false') { return false; } return !!i || /^(true|t|1)$/i.test(i); } else if (typeof i === 'number') { return i > 0; } else if (i instanceof Object) { return true; } return false; } }, string(i: mixed): ?string { if (typeof i !== 'undefined' && i !== null && i !== '') { return String(i); } }, enumerable<+E: string | number>(arr: $ReadOnlyArray): mixed => ?E { return (i: mixed) => { if ((typeof i === 'string' || typeof i === 'number') && i in arr) { // flow не корректно обрабатывает `i in arr` // Тут все верно работает // $FlowFixMe return i; } }; }, }; export function normalize( data: mixed, schema: O ): $ObjMap { let newData = {}; Object.keys(schema).forEach(key => { // Проверка нужна так как data может не быть или это будет null // так как typeof null === 'object'; const param = data instanceof Object ? data[key] : undefined; newData[key] = schema[key](param); }); return newData; }