JSDoc & Typescript

JSDoc & Typescript

Introduction

At Township, we prioritize maintainable and comprehensible code. To achieve this, we use JSDoc, a standardized and structured documentation tool. While some may view individual function documentation as redundant, we believe it is invaluable for saving time, avoiding confusion, and enhancing comprehension, especially for complex or critical code. Well-placed docs serve as a reference for future contributors and simplify documentation generation for handoff projects. As responsible engineers, we consider the perspectives of other team members and our future selves when incorporating documentation in our codebase.

Documenting TypeScript at Township

At Township, we are committed to producing maintainable and easy to comprehend code. We achieve this objective by deliberately naming variables and functions, utilizing pattern-matching techniques throughout the codebase, and implementing thorough documentation. While documentation within a codebase can be perceived as superfluous, especially for those with a comprehensive understanding of its inner workings, it can prove to be an invaluable tool for saving time and avoiding confusion for other developers. Additionally, well-placed documentation can serve to refresh the memory on older aspects of the codebase and provide simpler solutions for generating documentation for projects that may be handed off.

While well-written code can reduce the need for documentation in every function of an application, judiciously placed doc strings that elucidate the more complex and critical aspects of the code can prove to be an invaluable time-saver and enhance comprehension for newer contributors. Ultimately, the responsibility for determining where to incorporate documentation lies with the engineer, who should consider the perspective of other team members who may lack the same level of familiarity with the code, as well as their own future self who may need to revisit and refactor their work in six months' time.

Documentation Process at Township

You can find a comprehensive guide to JSDoc here. The basic format of JSDoc typically looks something like this:

/** 
  * A brief description of what the function is doing 
  * 
  * @param {object} name of obj param - brief description 
  * @param {boolean} name of bool param - brief description 
  * @returns {returnType} - description of what this function returns 
  */ 
function foo(arg1: object, arg2: boolean): returnType {
...
}

Here is an example of a well-documented function that makes use of function docs and smaller doc strings within the function itself.

/**
 * Fetches all appointments and pre-fetches data for each appointment. 
 * Returns early if not connected to a network. before returning, a lastPreFetch
 * timestamp will be updated and saved to device storage.
 * 
 * @async
 * @param {boolean} isOnline - A boolean indicating if the user is online.
 * @returns {Promise<void>} - A Promise that resolves when pre-fetching is complete.
 */ 
const prefetchAllAppointments = async (isOnline: boolean): Promise<void> => {
  if (!isOnline) return;

  setIsPreFetching(true);

  for (let i = 0; i < data.length; i++) {
    await prefetchAllDataForAppointment(data[i].id);
  }
	
	// set timestamp & save timestamp to lastPreFetch ref
  setSingleStore('lastFetch', dayjs().local().format('h:mm A'));
  lastPreFetch.current = await getSingleStore('lastFetch'); 

  setIsPreFetching(false);
};

Sometimes full JSdoc functions can be overkill, but a smaller doc string is still beneficial, for example here is a well documented file with multiple time related helper functions. Working with dayjs and timestamp formatting can be confusing at first glance, a few small doc strings can go a long way.

import dayjs from 'dayjs';

// formats timestamp to 12 hour time from a timestamp
// IE 2023-07-17T06:30:00.000-07:00 -> 6:30 AM
export function formatTimeSamp(t: string): string {
  return dayjs(t).format('h:mm A');
}

// Get the timezone of the current user
export function getUserTimezone() {
  return dayjs().startOf('date').toDate().toString();
}

// returns a string timestamp of 00:00 (local timezone) today.
// IE -> Mon Jul 17 2023 00:00:00 GMT-0700
export function startOfDay() {
  return dayjs().startOf('day').toDate().toString();
}

// returns a string timestamp of 23:59 (local timezone) today.
// IE -> Mon Jul 17 2023 23:59:59 GMT-0700
export function endOfDay() {
  return dayjs().endOf('day').toDate().toString();
}

// returns a formatted time to WeekDay, Month, #Day, Year
// 2023-07-17T06:30:00.000-07:00 -> Monday July 17, 2023
export function formatToDayMonthYear(t: string): string {
  return dayjs(t).format('dddd MMMM D, YYYY');
}

// Converts and returns the number of minutes to a readable format
//   0 -> 00
//   5 -> 05
//   30 -> 30
export function formatMinutes(t: number): string {
  if (t === 0 || t === 5) {
    return `0${t}`;
  }
  return t.toString();
} 

Additional Reading