All files type.ts

100% Statements 63/63
100% Branches 48/48
100% Functions 11/11
100% Lines 50/50

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 931x 1x         1x 36x     1x 9x   1x 51x   17x   15x   3x   4x   3x   1x   7x 1x     28x 1x 88x 88x 88x 2x 2x 2x     88x   88x   79x 52x 1x     51x     27x 5x 5x       22x   20x 26x   10x 26x   9x   8x     10x   9x   6x   4x   3x 2x 2x 1x     1x     1x    
import { combineValidationObjects } from './validate.js'
import {
  Validation, isArray, isEnum, isObj,
  isString, isMap, isNumber, isTypeDefValidation, ValueTypes, isMeta, isAnd
} from './validationTypes.js'
 
const containsOptional = (input: Validation) =>
  (Array.isArray(input) && input.some(y => y === '?')) ||
   input === '?'
 
const allOptional = (input: Validation) =>
  Object.values(input).every(containsOptional)
 
const simpleTypes = (input: string) => {
  switch (input) {
    case 'string':
      return 'string'
    case 'number':
      return 'number'
    case 'integer':
      return 'number'
    case 'boolean':
      return 'boolean'
    case 'any':
      return 'any'
    case 'null':
      return 'null'
    case '?':
      return 'undefined'
    default: throw new Error(`Unhandled ${input}`)
  }
}
export const validationToType = (input:ValueTypes): string => validationToTypeInternal(input, {})
const validationToTypeInternal = (input: ValueTypes, typesIn: {[key:string]:Validation}): string => {
  let customTypes = typesIn
  let type:ValueTypes = input
  if (isTypeDefValidation(input)) {
    customTypes = input.$types
    type = { ...input }
    delete type.$types
  }
 
  const toType = (input:ValueTypes) => validationToTypeInternal(input, customTypes)
 
  if (Array.isArray(type)) { return type.map(toType).join(' | ') }
 
  if (typeof type === 'string') {
    if (customTypes[type]) {
      return toType(customTypes[type])
    }
 
    return simpleTypes(type)
  }
 
  if (isArray(type)) {
    const typeRet = toType(type.$array)
    return (Array.isArray(type.$array) && type.$array.length > 1) || typeRet.indexOf('|') > -1
      ? `(${typeRet})[]` : `${typeRet}[]`
  }
 
  if (isEnum(type)) { return type.$enum.map(x => `"${x}"`).join(' | ') }
 
  if (isObj(type)) {
    const optionalPostfix = (value: Validation) => containsOptional(value) ? '?' : ''
 
    const obj = Object.entries(type)
      .map(([key, value]) => `${key.startsWith('\\$') ? key.slice(1) : key}${optionalPostfix(value)}: ${toType(value)}`)
      .join('; ')
    if (allOptional(type)) { return `{ ${obj} } | undefined` }
 
    return `{ ${obj} }`
  }
 
  if (isString(type)) { return toType('string') }
 
  if (isMap(type)) { return `{ [key: string] : ${toType(type.$map)}}` }
 
  if (isMeta(type)) { return toType(type.$type) }
 
  if (isNumber(type)) { return toType('number') }
 
  if (isAnd(type)) {
    const combined = combineValidationObjects(type, customTypes, (x) => x)
    if (combined.result === 'error') {
      throw new Error('Schema error, $and types must be objects: ' + JSON.stringify(combined.error, null, 2))
    }
 
    return toType(combined.pass)
  }
 
  throw new Error(`UNSUPPORTED ${JSON.stringify(type, undefined, 2)}`)
}