Skip to content

Instantly share code, notes, and snippets.

@fabecerram
Created November 9, 2023 19:18
Show Gist options
  • Save fabecerram/937bb807a7f50136135c6874b8346fb9 to your computer and use it in GitHub Desktop.
Save fabecerram/937bb807a7f50136135c6874b8346fb9 to your computer and use it in GitHub Desktop.
TypeScript Programming Styles Guide

TypeScript Programming Styles Guide

A style guide tells a developer how to work with a particular programming language. Programming style guides are composed of rules and guidance. Rules are mandates, with very few exceptions, enforceable throughout the entire IT organization.

Rules make good and bad programming behavior explicit. Guidance is a recommended practice or strategy. Guidance allows for more leeway than rules.

A coding style guide might inform programmers on such issues as acceptable naming conventions, how to store source files, and code format. Some rules border on the mundane, but they collectively serve larger organizational goals.

The main purpose of naming conventions is to keep your code and your work organized. Without any structure in your naming, analyzing large volumes of code or information could become chaotic.

In general, provides a set of rules for choosing the sequence of characters to identify variables, functions, and other elements within code or its documentation.

Basic Naming rules

The name of a variable, function, class, or any other element should answer all the big questions. It should tell you why it exists, what it does, and how it is used. If a name requires a comment, then the name does not reveal its intent.

  • Use meaningful names.

Distinguish names in such a way that the reader knows what the differences offer.

Bad:

function isInRange(a1: number, a2: number, a3: number): boolean {
  return a2 <= a1 && a1 <= a3;
}

Good:

function isInRange(value: number, left: number, right: number): boolean {
    return left <= value && value <= right;
}


  • Use pronounceable names

If you can't pronounce it, you can't discuss it without sounding weird.

Bad:

class Sub {
  public ccId: number;
  public bilAddId: number;
  public shpAddId: number;
}

Good:

class Subscription {
  public creditCardId: number;
  public billingAddressId: number;
  public shippingAddressId: number;
}


  • Avoid mental mapping

Explicit is better than implicit. Clarity is king.

Bad:

const u = getUser();
const s = getSubscription();
const t = charge(u, s);

Good:

const user = getUser();
const subscription = getSubscription();
const transaction = charge(user, subscription);


  • Don't use negative names for boolean variables.

Bad:

const isNotEnabled = true;

Good:

const isEnabled = false;

A prefix like is, are, or has helps every developer to distinguish a boolean from another variable by just looking at it.

Bad:

const enabled = false;

Good:

const isEnabled = false;


  • Don't add unneeded context

If your class/type/object name tells you something, don't repeat that in your variable name.

Bad:

type Bicycle = {
  bicycleBrand: string;
  bicycleModel: string;
  bicycleColor: string;
}

function print(bicycle: Bicycle): void {
  console.log(`${bicycle.bicycleBrand} ${bicycle.bicycleModel} (${bicycle.bicycleColor})`);
}

Good:

type Bicycle = {
  brand: string;
  model: string;
  color: string;
}

function print(bicycle: Bicycle): void {
  console.log(`${bicycle.brand} ${bicycle.model} (${bicycle.color})`);
}


  • Don't use underscore ( _ ) as prefix/suffix

Identifiers must not use _ as a prefix or suffix. This also means that must not be used as an identifier by itself, or to indicate a parameter is unused.

Bad:

_name: string;
lastName_: string;

const [a,_,b] = [1, 5, 10];

Good:

name: string;
lastName: string;

const [a, ,b] = [1, 5, 10];


  • Don't add unnecessary or odd prefixes.

The syntax of the language facilitates the identification of most of the elements and their characteristics, it is not necessary to add redundant prefixes, or to invent strange prefixes to highlight some particular element, that only makes the reading more difficult.

Example: Dot use the "opt_" prefix for optional parameters.

Bad:

function multiply(firstNumber: number, secondNumber: number, opt_thirdNumber?: number): number { ... }

Good:

function multiply(firstNumber: number, secondNumber: number, thirdNumber?: number): number { ... }


Naming Conventions


  • Variable Names: Use camelCase for names.
firstNumber:number = 12;
isActive: boolean = false;

  • Constants: Use UPPER_CASE with underscore '_' between multiple words.
const FIRST_NUMBER:number = 18

  • Method Names: Use camelCase. The names of methods should be lucid and descriptive, reflecting the method's purpose.
getEnvironmentPath()

  • Class: Use PascalCase for class names, names should be a noun or noun phrase.
AppConfig

  • Class members: Use camelCase for names.
class Foo {
  quantity: number;
  isActive: boolean = false;
  ...
}

  • Interface: Use PascalCase for interface names, names should be a noun or noun phrase. Refrain from using the "I" prefix or "Interface" suffix in the interface name like IUser or UserInterface.
interface User {
  printUserName():void;
}

  • Type: Use PascalCase for names. Types provide a means to establish custom types in TypeScript. They can create aliases for existing types or generate new types from the ground up. The names of types should be lucid and descriptive, reflecting the type's purpose.

For object types , follow the same conventions as for interfaces.

type Developer = {
  name: string;
  languages: Language[];
}

For union types , prefer using descriptive noun phrases and the conjunction "Or" to connect them.

type DeveloperOrDesigner = Developer | Designer;

However , whenever possible, avoid using the conjunction "Or" and opt for more descriptive names.

type Employee = Developer | Designer;

  • Function types: Use PascalCase for names. Function types define the structure of a function, including its parameters and return type. Use a verb or verb phrase that indicates the action performed by the function.
// function type
type UserGenerator = (username: string, email?: string) => User;

// function type overloading
type UserGenerator = {
  (username: string): User;
  (username: string, email?: string): User;
};

  • Namespace: Use PascalCase for names.
namespace Validation {

  export interface StringValidator {
    isAcceptable(s: string): boolean;
  }
}

  • Enum: Use PascalCase for enum names. Short for enumeration, enums enable the definition of a set of named constants. Use a singular noun or noun phrase that describes the set of named constants being defined.

Use PascalCase or CONSTANT_CASE for every property in the enum.

enum Role {
  ADMIN,
  MODERATOR,
  USER,
}

// or

enum Month {
  January,
  February,
  ...
}

  • Generics: Use PascalCase for class names. A component of generic programming, allow developers to create reusable code compatible with various data types. This is a special case regarding the names of class members, therefore:

When using only one generic type, represent it with a single uppercase letter.

interface Response<S, T, U> {
  status: S;
  data: T;
  error: U;
}

If the generic type is used multiple times, or if there are several generic types, consider using descriptive names.

interface Response<TStatus, TResult, TError> {
  status: TStatus;
  data: TResult;
  error: TError;
}

  • File Names: Use lower-case (Separated by '-' if the name is of two/more words)
employee-details

Other conventions


  • Quotes: there is no particular standard to use quotes in Typescript, in fact, you can use both, single and double quotes, but in the JavaScript ecosystem the use of single quotes is the most widely adopted.

Prefer single quotes (').

name = 'John Doe';

Using back ticks (`) if contain interpolated values.

name = "world";
greeting = `hello ${name}`;

Using "\" to escape quotes in string

message = 'It\'s a beautiful day';

Double quotes are not without merit: Allows easier copy paste of objects into JSON. Allows people to use other languages to work without changing their quote character. Allows you to use apostrophes e.g. He's not going.. But I'd rather not deviate from where the JS Community is fairly decided.


  • Spaces: Use 2 spaces. Not tabs. The standard in JavaScript is use 2 spaces, the TypeScript/VSCode teams use 4 spaces by default, but are definitely the exception in the ecosystem.

  • Semicolons: Use semicolons. Explicit semicolons helps language formatting tools give consistent results.
interface Point {
  x: number;
  y: number;
}

  • Brackets: Use OTBS (one true brace style).

The one true brace style is one of the most common brace styles in TypeScript, in which the opening brace of a block is placed on the same line as its corresponding statement or declaration. Always wrap the body of the statement in curly brackets.

if (foo) {
  ...
}
else {
  ...
}

  • No line continuations: Do not use line continuations (that is, ending a line inside a string literal with a backslash) in either ordinary or template string literals. Even though ES5 allows this, it can lead to tricky errors if any trailing whitespace comes after the slash, and is less obvious to readers.

Bad:

const LONG_STRING = 'This is a very long string that far exceeds the 80 \
column limit. It unfortunately contains long stretches of spaces due \
to how the continued lines are indented.';

Good:

const LONG_STRING = 'This is a very long string that far exceeds the 80 ' +
'column limit. It does not contain long stretches of spaces since ' +
'the concatenated strings are cleaner.';

  • Constructors: is a special method that is used to initialize objects. The constructor is called when an object of a class is created. It can be used to set initial values for object attributes.

Constructor calls must use parentheses, even when no arguments are passed.

const x = new Foo();

It is unnecessary to provide an empty constructor or one that simply delegates into its parent class because ES2015 provides a default class constructor if one is not specified.

class UnnecessaryConstructor {
  constructor() {}
}

class UnnecessaryConstructorOverride extends Base {
  constructor(value: number) {
    super(value);
  }
}

However constructors with parameter properties, visibility modifiers or parameter decorators should not be omitted even if the body of the constructor is empty.

class ParameterProperties {
  constructor(private myService) {}
}

class ParameterDecorators {
  constructor(@SomeDecorator myService) {}
}

  • No "#private" fields: Do not use private fields (also known as private identifiers), they are unsupported before ES2015, and cause substantial emit size and performance regressions when down-leveled by TypeScript.

Bad:

class Foo {
  #id = 1
}

Good:

class Foo {
  private id:number = 1;
}
  • Comments: they are used to include information on portions of source code in a project. Code comments are intended to make the related code easier to understand by providing additional context or explanation. We have two ways to add comments in Typescript:
Simple line comments

// This is a simple line comment
this.parseProducts(products);

Multi line comments

/*
 This is a multi
 line comment
*/
constructor()

It is important to respect the best practices and recommendations on how to comment and document the source code.


Documentation conventions

Code documentation is a collection of documents and code comments explaining how code works and how to use it. The form and size of documentation can vary.

Many developers don’t recognize the value of documenting code. They might argue that good code is self-explanatory. However, accurate documentation is essential for maintaining a codebase because it allows developers to quickly understand what the code does and how to work with it.

Good documentation is especially needed when many developers use code inside or outside your organization. Taking the time to document the code will make their work easier, and they’ll appreciate it. Good documentation should save more time than you spend writing it. In some cases, writing documentation can help identify overly complicated parts of the code and improve your architecture.

Speaking specifically about Typescript, the ideal is to follow the standards, it makes things easier for us and also helps the tools that specialize in compiling this documentation work better.

I personally recommend using TSDoc, is a proposal to standardize the doc comments used in TypeScript code, so that different tools can extract content without getting confused by each other's markup.

If our project includes api rest, it is recommended to use Swagger to keep our api properly documented.


Creator

Fabian A. Becerra M. https://github.com/fabecerram


Copyright and license

Code and documentation copyright 2019-2023 the authors. Code released under the MIT License.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment