Cheat Sheet: JavaScript (TypeScript) variable naming convention

Cheat Sheet: JavaScript (TypeScript) variable naming convention

Recommendations on how to unify naming convention within a code – formats, acronyms and abbreviations, value-based prefixes, event handlers, etc. Mainly for vanilla TypeScript with a bit of React.

Variable name formats

It is good to restrict name formats to those listed below unless there is an extraordinary reason (e.g. pairing with database column names):

  • camelCase – default name format,

  • PascalCase – format of classes, types, enum-like structures, interfaces and special structures (e.g. React component names),

  • SCREAMING_SNAKE_CASE – global constants.

We can use full words (properties), abbreviations (props) and acronyms within variable names.

  • 2 letter acronyms are uppercase.

  • 3+ letter acronyms are lowercase.

// 2 letters
const TranslationsEn = {};  // abbreviation
const enTranslations = {};  // abbreviation
const adminUI = {};         // acronym
const adminUIAdvanced = {}; // acronym
const UIButton = {};        // acronym

// 3+ letters
type ComponentProps = {};  // abbreviation
const htmlTags = [];       // acronym
const copyHtml = () => {}; // acronym

Custom values

All variables must be named to reflect the meaning of the stored value. The name format is chosen according to the list above. In special cases (boolean values, enums, etc.), variable names may use a unique prefix or rule to distinguish themselves from the rest.

// <variableDescription>
const result = 1;
const nextMiddleware = "logger";

// <VARIABLE_DESCRIPTION>
const ENVIRONMENT_VARIABLE = process.env.ENVIRONMENT_VARIABLE;

Boolean values - is, are, has

These variables should only contain true/false boolean (not string) values and thus implicitly declare that they are safe to use in conditions.

// (is|are)[<Noun>]<Adjective>
const isSubmitted = false;
const isFormASubmitted = false;
const isFormBSubmitted = false;
const areFormsSubmitted = isFormASubmitted && isFormBSubmitted;

// (is|are)<Action>
const isFetching = true;

// has<SubjectDescription>
const hasAtLeastOneOddNumber = true;

// Global constant with IS prefix
const IS_DEBUG_MODE = true;

Prefixes is/are have a priority over has – don't name a variable hasDefinedStatus when you can use isStatusDefined. The prefix has should be used in cases that cannot be comfortably replaced with is/are, e.g. hasTwoElementsFromArray.

Negation

Sometimes, negation is used as frequently as the original value, or we need to operate with negative statements only. In these cases, we can use a secondary Not prefix.

const isFormatted = false;
const isNotFormatted = !isFormatted;

Enums

Enums have a special behaviour - TypeScript enum as a type is not the best concept, according to the TypeScript Team. Therefore it is better to use an object with as const structure (explanation). According to Google JS Guide, we can think of this object as a special structure (PascalCase) containing constants (SCREAMING_SNAKE_CASE).

const RequestStatus = {
   PENDING: 'Pending',
   SUCCESS: 'Success',
   ERROR: 'Error',
} as const;

type RequestStatus = (typeof RequestStatus)[keyof typeof RequestStatus];

// Usage
RequestStatus.SUCCESS;

Types, interfaces and generics

Types use PascalCase and do not need a special prefix. Interfaces and generics have one letter prefix because of their special use cases (useful mainly in libraries).

// <TypeName>
type SystemProps = {};

// I<InterfaceName>
interface ISystemProps {}

// T<GenericType>
const createValue = <TInputValue>(value: TInputValue) => {
   return value;
};

Event handlers

Event handlers (callbacks) are functions that are called after an event – e.g. HTML onclick, oncontextmenu, React events, etc.

// handle[<Object>]<Action>
const handleClick = () => {};           // click is clearly defined
const handleSendButtonClick = () => {}; // one of many click events
const handleSignOut = () => {}          // multi-usage close callback
  • Object - defines unique items that we interact with. It is optional if the handler is called from multiple places or if it is unique within the code part.

  • Action - an action caused by a user.

React applications are a special case using onX={handleX} pairs.

type ComponentProps = {
   onSignInFormSubmit: () => {};
};

const Component = ({ onSignInFormSubmit }: ComponentProps) => {};

const Parent = () => {
   const handleSignInFormSubmit = () => {};
   return <Component onSignInFormSubmit={handleSignInFormSubmit} />
}

It is better to not name handlers with on prefix unless you, for example, want to create an auto-spreading structure of build-in properties (using spread syntax with prop names onClick, etc.). Detailed explanation: Event Handler Naming in React (by Jake Trent).

Functions

A function is always some kind of action. Therefore, its name should begin with a verb. It is a good idea to fix a set of these verbs and describe their purpose.

// transform[Entity]to[Entity]
const transformResultToOrderedArray = () => {}

// format[Entity]As[Entity]
const formatTimestampAsLocalizedEnString = () => {}

// (generate|gen)[Entity]
const generateRandomTraceId = () => {}
const genRandomTraceId = () => {}

HTTP method functions (return a result of fetch API) should be named with a method that they return:

// <method><Entity>
const getArticles = () => {};
const postArticle = () => {};
const putArticle = () => {};
const patchArticle = () => {};
const deleteArticle = () => {};
// ...

Class private attributes and methods

According to Private Fields we need to use # (hash names) for private methods and attributes.


class PrivateNumber {
    #x;

    constructor(x) {
        this.#x = x;
    }

    get #privateSecretValue() {
        return this.#x + 10;
    }

    get secretValue() {
        return this.#privateSecretValue;
    }
}

Conclusion

After all, it is not so important which style, rules or prefixes we use. They just need to be consistent at least within a project. But we make our (and our colleagues) lives easier when we use at least somehow unified conventions.