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 precise constraints with convenient error messages in JavaScript. For example:

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

That function is for formatting UI Rig’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.

const AuthError = 1
const ValidationError = 2

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)
    ))
      throw ValidationError

    if (!await passwordResetTokenExists(body.token, body.userId))
      throw AuthError

    // …
    sendOk(response)
  }

  catch (error) {
    switch (error) {
      case AuthError:       sendUnauthorized(response); break;
      case ValidationError: sendBadRequest(response);   break;
      default:              sendInternalServerError(response);
    }
  }
}
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: