DEV Community

Octoxalis
Octoxalis

Posted on • Originally published at typexjs.netlify.com

JavaScript naming scheme: TypexJS

One of the most useful principle of software development which should be permanently kept in mind is not a new one. Actually, it's an almost millenial assertion: Entities must not be multiplied without necessity, a sentence frequently associated with the Occam's razor concept. Despite his canonical age, the validity of the sentence remains intact in the sofware engineering field and it has been adaptated in many ways (the KISS principle is one among many others).

Therefore, what are the implications of such a law for a JavaScript programmer? And why is a computer language typing concept related to it?

A dispute about names and types

Every developer knows that JavaScript is not a static typed language, a useful feature eliminating lots of bugs (a language like Typescript has been created as a remedy to that important lack of safety). Even for code modules counting less than a few tens of lines, it's easy to forget what exactly is the type of a variable declared at the begining of the file and then make a mistake when assining a wrong type to that variable.

TypexJS is a simple convention designed to avoid such mistakes: by only adding a mnemonic letter at the end of each variable identifier to specify its type (of course, this scheme also applies to constants).

This simple adjonction has two main benefices:

  • it gathers tightly related variables in a consistent semantic field;
  • it simplifies identifier derivations.

Just an example, taken from MDN String documentation. The JavaScript String.prototype.split method returns an array of Strings:

var str = 'The quick brown fox jumps over the lazy dog.';
var words = str.split(' ');

Two different words for two tightly related entities! Isn't it semantically more meaningful to use the same identifier with different specifiers?

const sentence_s = 'The quick brown fox jumps over the lazy dog.';
const sentence_a = sentence_s.split(' ');

A more tricky example (with a smart inline type coercion tricks!):

let number_s = '123'
let number_n = +number_s    //: cast to Number
number_s = '' + ++number_n  //: cast to String

Types specifiers

TypexJS naming scheme applies to all primitive immutable types:

  • Null
  • Undefined
  • Boolean
  • Number
  • BigInt
  • String
  • Array
  • Object
  • Symbol

It extends to specific Object types:

  • Class
  • RegExp
  • Function

The suffix specifying the type is a single letter preceded by an underscore character:

  • lowercase letters for "wrapped" types: Boolean, Number, BigInt, String, Array, Object, Symbol, Function, Class, RegExp (see MDN Primitive description)
  • uppercase letters for "unwrapped" types: Null, Undefined.
Type Suffix
Null _N
Undefined _U
Boolean _b
Number _n
BigInt _i
String _s
Array _a
Object _o
Symbol _y
Class _c
RegExp _r

To convey even more meaningful information, function identifiers follow a slightly different scheme: two underscore characters before the type specifier of the returned value. Consequently, if a function doesn't return anything (void function), the v letter is used for its suffix!

Function return is Suffix
Null __N
Undefined __U
Boolean __b
Number __n
BigInt __i
String __s
Array __a
Object __o
Symbol __y
Class __c
RegExp __r
Function __f
Void __v
const awesome__s = () => 'An awesome String'
const clone__s = awesome__s

In case of a function returning different types of value, we just omit the type character:

const silly__ = string_b => string_b ? 'A weird String' : 1234

We could do the same for a variable accepting different kinds of type, but is it really a good practice?

let hybrid_ = 'A String'
hybrid_ = 1234  //: What a mess!

Full example

const DOM_siblings__a = selector_s =>
{
  const node_e = document.querySelector( selector_s )
  return !node_e ?
    null :
    Array.prototype.filter
      .call( node_e.parentNode.children, sibling_e => sibling_e !== node_e )
}

const DOM_listReverse = selector_s =>
{
  const node_a = Array.prototype.slice.call( document.querySelectorAll( `${selector_s} li` ))
  node_a.forEach( node_e => node_e.parentNode.insertBefore( node_e, node_e.parentNode.firstChild ) )
}

The previous code shows an exception to the specifiers convention used by TypexJS: for DOM elements, it seems more appropriate to use the _e suffix (for element, because the _N suffix is already used for a Null value) than the _o suffix used for Object.

The example code shows another singularity: we can instantly differenciate JavaScript and browser specific functions or methods from our own code functions and methods. Our identifiers have a letter suffix where JS and browser identifiers have nothing. Therefore, we can safely use exactly the same names:

const myWindow = window  // usual way
const window_o = window  // smarter isn't it?

Hence, everytime we want to override some JavaScript or DOM function
(or any browser specific, third-party library function, etc.), we can safely do it while keeping a semantic coherency with the original function.

This very simple and easy to memorize convention can help you to write a better code while conveying more information about it as well as shortening its documentation. You can find more details about TypexJS visiting its dedicated site and put comments or questions if you see fit.

Top comments (0)