Types in plain JavaScript

by Eric Fortis
Typed languages are fine, as far as they go. But unless the type system is Turing complete, type correctness cannot imply program correctness. — Uncle Bob

Let’s explore how we can validate types and constraints with convenient error messages. For example, in TypeScript we would String check cardId and Number check decimals, which is fine, but we could take it a step further.

function updateCardDecimals(cardId, decimals) {
  isCard(cardId)
  isValidDecimal(decimals)
  setCard(cardId, CF.decimals, decimals)
}

For context, that function is for formatting Uxtly’s formulas.

Update Decimals of the Formula Total

isCard

Let’s ensure that cardId is a validly formatted string, and assert the card exists in the collection.

function isCard(cardId) {
  if (!/^[a-z]+$/i.test(cardId) ||
      !Object.hasOwn(cardsCollection, cardId))
    throw new CardNotFound(cardId)
}

class CardNotFound extends ReferenceError {}

isValidDecimal

In addition to the integer check, we can verify that the value is in the allowed numeric range.

function isValidDecimal(value) {
  if (!Number.isInteger(value) ||
      value < 0 || 
      value > MAX_FRACTION_DIGITS)
    throw RangeError(`Invalid Decimal "${value}"`)
}

BTW, the JavaScript Error Types:

EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError, AggregateError

Server Side Example

The following function responds with Bad Request HTML when the number of fields in the payload, or their names don’t match the expectation. Also, it examines the values beyond their data type.

async function resetPasswordPost(request, response) {
  try {
    const body = await jsonBody(request)

    if (!(
      body
      && Object.keys(body).length === 3
      && isPassword(body.password)
      && isId(body.userId)
      && isId(body.token)
    )) {
      Send.BadRequest(response)
      return
    }

    if (!await passwordResetTokenExists(body.token, body.userId)) {
      Send.Unauthorized(response)
      return
    }

    // …
    Send.Ok(response)
  }
  catch (error) {
    Send.InternalServerError(response, error)
  }
}
function isId(value) {
    return check(value, String)
    && value.length === StandardIdCharLength
    && /^[\w-]*$/.test(value)
}

Open Source

Check out this library: type-check

check('a', String)
check(100, Number)
check(new Int8Array([1, 2, 3]), Int8Array)

Sponsored by: