Typescript Exception Free Function Error Handling | Task

Ole Ersoy
Oct - 01  -  3 min

Scenario

In the article The Power of the Simplest Possible Example we implemented the median function per the illustration of the method used to calculate it.

Here we will be adding error handling without using exceptions.

Approach

Error Function Type

Error strings will be created by functions that have the signature:

export type MessageFunctionType = (args?: string[]) => string;

Result Object

We’ll create a class to contain the result and errors if there are any.

/**
 * The result of the calculation.
 */
export class Result<E> {
    public message?:string
    constructor(
        public value: E | undefined,
        public error?: MessageFunctionType,
        public parameters?: string[]
    ) { 
        if (error) {
            this.message = this.error!(parameters)
        }
    }
}

In the event of an error the error property is assigned the function of type MessageFunctionType . The error property can be used in a switch statement to select the type of error that occurred.

Errors

We will define two types of errors. The first indicates that no array was passed in to the median function. The second indicates that one of the values in the array was not a number:

export interface IMedianErrors {
    ARRAY_ARGUMENT_NOT_DEFINED: MessageFunctionType;
    ARRAY_VALUE_NOT_A_NUMBER: MessageFunctionType;
}

And the implementation for the interface looks like this:

export const MEDIAN_ERRORS: IMedianErrors = 
{
   ARRAY_ARGUMENT_NOT_DEFINED: (arr?: string[]) => {
      return `The array argument for the median function was missing.`;
},
    ARRAY_VALUE_NOT_A_NUMBER: (arr?: string[]) => {
       return `The array ${ arr && arr.toString() } contains values that are not numbers.`;
}
};

Median Function Implementation

We are now communicating both the result and any errors that occurred using a MedianResult instance:

/**
 * @param arr The set of numbers
 */
export function median(arr?: any[]): MedianResult {
  //=============================
  // MISSING ARRAY
  //=============================
  if (!arr) {
    const result = new MedianResult(
      NaN,
      MEDIAN_ERRORS.ARRAY_ARGUMENT_NOT_DEFINED,
      []
    );
    return result;
  }
  //=============================
  // NON NUMERIC VALUES
  //=============================
  let numeric = true;
  arr &&
    arr.forEach((n) => {
      if (!isNumeric(n.toString(), {})) {
        numeric = false;
      }
    });
  if (!numeric) {
    const result = new MedianResult(
      undefined,
      MEDIAN_ERRORS.ARRAY_VALUE_NOT_A_NUMBER,
      arr.map((e) => e.toString())
    );
    return result;
  }

  if (!(arr && arr.length)) {
    //=============================
    // EMPTY ARRAY
    //=============================
    const result = new MedianResult(undefined);
    return result;
  } else {
    //=============================
    // LENGHT OF 1
    //=============================
    if (arr.length == 1) {
      const result = new MedianResult(arr[0]);
      return result;
    }
    //=============================
    // PROCESS EVEN OR ODD SET OF NUMBERS
    //=============================
    const mid = Math.floor(arr.length / 2);
    const sorted = [...arr].sort((a, b) => a - b);
    const m =
      arr.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
    const result = new MedianResult(m);
    return result;
  }
}

Demo