Clean Code Names in TypeScript
Names are everywhere in software. We name our variables, functions, arguments, classes, modules, source files and directories. Because we do so much of it, we’d better do it well.
Variables
A variable name should accurately identify your variable. When you create good variable names, your code becomes easier to understand and easier to work with
- Variable names cannot contain spaces.
- Variable names must begin with a letter, an underscore (_) or a dollar sign ($).
- Variable names can only contain letters, numbers, underscores, or dollar signs.
- Variable names are case-sensitive.
const f = "John" //name of user
The variable name f tells us nothing. We can do better.
const firstName = "John"
Avoid names with meaning from other areas, example, hp, un, ps, pg, ms. These are all poor names which have meanings somewhere else.
Avoid names of groupings of data as lists, example, accountList unless it’s actually a list.
Avoid names which vary in small ways, example, XYZControllerForEfficientHandlingOfStrings and XYZControllerForEfficientStorageOfStrings. Autocomplete on IDE may cause you to choose the unintended one by mistake
Use meaningful names take the following example:
function copyChars(a1: string[], a2: string[]){
for(let i = 0; i < a1.length, i++){
a2[i] = a1[i]
}
}
This function reads much better with source and destination
function copyChars(source: string[], destination: string[]){
for(let i = 0; i < source.length, i++){
destination[i] = source[i]
}
}
Functions
Functions need to be treated the same. We must show intent when we declare a function
const list1 = [ [4,1,1], [0,1,2], [0,1,3] ]
function getData(): number[][]{
let list2 : number[][] = []
list1.forEach(item => {
if(item[0] === 4){
list2.push(item)
}
})
return list2;
}
const data = getData()
console.log(data)
It’s so hard to tell what this code is doing. There are no complex expressions. The problem is that the code requires you to know stuff about the data like:
- What kinds of things are in list1
- What is the significance of the zeroth subscript of an item in list1
- What is the significance of the value of 4
- How would I use the list being returned
The answers to these questions are not present in the code sample, but they could have been. Say that we’re working in a mine sweeper game. We find that the board is a list of cells called list1. Let’s rename that to gameBoard.
Each cell on the board is represented by a simple array. We further find that the zeroth subscript is the location of a status value and that a status value of 4 means “flagged.” The remaining subscripts are the co-ordinates. Just by giving these concepts names we can improve the code considerably:
const gameBoard = [ [4,1,1], [0,1,2], [0,1,3] ]
function getFlaggedCells(): number[][]{
const FLAGGED = 4
const STATUS_VALUE = 0
let flaggedCells : number[][] = []
gameBoard.forEach(cell => {
if(cell[STATUS_VALUE] === FLAGGED){
flaggedCells.push(cell)
}
})
return flaggedCells;
}
const flaggedCells = getFlaggedCells()
console.log(flaggedCells)
Notice that the simplicity of the code has not changed but the code has become much more explicit.
We can go further and map our gameboard to Cell objects. It can include an intention-revealing cell property (isFlagged)
interface Cell{
isFlagged: boolean
x: number
y: number
}
const gameBoard: Cell[] = [ [4,1,1], [0,1,2], [0,1,3] ].map(cell => ({isFlagged: cell[0] === 4, x: cell[1], y: cell[2]}))
function getFlaggedCells(): Cell[]{
const FLAGGED = 4
const STATUS_VALUE = 0
let flaggedCells : Cell[] = []
gameBoard.forEach(cell => {
if(cell.isFlagged){
flaggedCells.push(cell)
}
})
return flaggedCells;
}
const flaggedCells = getFlaggedCells()
console.log(flaggedCells)
With these simple name changes, it’s not difficult to understand what’s going on. This is the power of choosing good names.