This article presents 8 unique tricks to help beginners write clean, professional, and maintainable TypeScript code.

We will also demonstrate how to apply these tricks to write clean code in TypeScript by building a simple To-Do application.

TypeScript is an open-source programming language created and managed by Microsoft, which functions as a rigorous syntactical expansion of JavaScript.

It provides the option of static typing to the language, resulting in early error detection during the development stage.

TypeScript code can be compiled into JavaScript, allowing it to operate on any browser or platform that supports JavaScript.

8 Tricks to write clean code in TypeScript

1. Use Interfaces for Object Shapes

Interfaces are essential tools in TypeScript for defining the shape of an object. They help ensure that an object adheres to a specific structure, making the code easier to understand and maintain.

interface Person {
  name: string;
  age: number;
}

function greet(person: Person) {
  console.log(`Hello, ${person.name}. You are ${person.age} years old.`);
}

2. Make use of Enums

Enums provide a convenient way to define a set of named constants. This makes it easier to represent and manage sets of related values in a type-safe manner.

enum Color {
  Red,
  Green,
  Blue,
}

function getColorName(color: Color): string {
  return Color[color];
}

3. Use Type Aliases

Type aliases enable developers to create custom names for existing types. This enhances code readability and simplifies maintenance.

type StringOrNumber = string | number;

function logValue(value: StringOrNumber) {
  console.log(value);
}

4. Adopt Tuples

Tuples allow you to represent an array with a fixed number of elements whose types are known but need not be the same. This provides type-safe handling of heterogeneous data structures.

type Point2D = [number, number];

function distanceFromOrigin(point: Point2D): number {
  const [x, y] = point;
  return Math.sqrt(x * x + y * y);
}

5. Leverage Union Types

Union types enable you to represent a value that could be one of several different types. This helps in writing flexible and type-safe code.

type Vehicle = "Car" | "Bike" | "Truck";

function getSpeed(vehicle: Vehicle): number {
  if (vehicle === "Car") return 120;
  if (vehicle === "Bike") return 80;
  return 60;
}

6. Utilize Optional Chaining:

Optional chaining helps access properties of potentially undefined or null values, making your code more concise and robust.

interface User {
  address?: {
    city?: string;
  };
}

const user: User = {};

const city = user.address?.city; // city will be undefined, no error

7. Implement Nullish Coalescing

Nullish coalescing allows you to provide a default value when a given value is null or undefined. This improves code readability and reduces the need for verbose conditional checks.

function logMessage(message: string | null | undefined) {
  const defaultMessage = "No message provided";
  console.log(message ?? defaultMessage);
}

8. Apply Generics

Generics allow you to write reusable and type-safe functions, classes, and interfaces without committing to a specific type upfront.

function identity<T>(arg: T): T {
  return arg;
}

const num = identity<number>(42);
const str = identity<string>("Hello");

Now that we have discussed these 8 unique tricks, let’s build a simple To-Do application to demonstrate their practical application.

1. Use Interfaces to define the model for To-Do items:

Interfaces help ensure that an object adheres to a specific structure, making the code more maintainable and readable. We’ll define an interface for a To-Do item:

interface ToDoItem {
  id: number;
  title: string;
  completed: boolean;
  priority: Priority;
}

2. Utilize Enums to define priority levels for To-Do items

Enums make it easier to work with related values by defining a set of named constants. We’ll define an enum for priority levels:

enum Priority {
  Low,
  Medium,
  High,
}

3. Create an array of To-Do items using Tuples:

Tuples are fixed-size arrays that can store values of different types. We’ll define an array to store To-Do items:

let toDoList: ToDoItem[] = [];

4. Write a function to add To-Do items to the list:

Add a To-Do item:

function addToDo(id: number, title: string, priority: Priority): void {
  const newItem: ToDoItem = { id, title, completed: false, priority };
  toDoList.push(newItem);
}

Get a To-Do item:

function getToDo(id: number): ToDoItem | undefined {
  return toDoList.find((item) => item.id === id);
}

Mark a To-Do item as completed:

function markAsCompleted(id: number): void {
  const item = getToDo(id);
  if (item) {
    item.completed = true;
  }
}

5. Use Optional Chaining to access the properties of To-Do items:

Optional Chaining allows you to access deeply nested properties without having to check for null or undefined values.

function getItemTitle(id: number): string | undefined {
  return getToDo(id)?.title;
}

6. Use Nullish Coalescing to provide a default value when a To-Do item is not found in the list:

Nullish Coalescing is an operator that returns the right-hand value if the left-hand value is null or undefined.

function getItemPriority(id: number): Priority | string {
  return getToDo(id)?.priority ?? 'Item not found';
}

7. Implement Generics to write reusable functions for sorting the To-Do list:

Generics allow you to create reusable functions that work with different types.

function sortBy<T>(key: keyof T, order: 'asc' | 'desc' = 'asc'): (a: T, b: T) => number {
  return (a: T, b: T) => {
    if (a[key] < b[key]) {
      return order === 'asc' ? -1 : 1;
    }
    if (a[key] > b[key]) {
      return order === 'asc' ? 1 : -1;
    }
    return 0;
  };
}

// Example usage:
toDoList.sort(sortBy<ToDoItem>('priority'));

We have now created a simple To-Do application using TypeScript that uses the 8 unique tricks discussed above to write clean code in TypeScript for better code readability, flexibility, and maintainability.

To run this TypeScript code, you’ll need to have TypeScript installed on your machine. You can install it using npm:

npm install -g typescript

Once TypeScript is installed, compile the todo.ts file using the tsc command:

tsc todo.ts

This will generate a todo.js file containing the compiled JavaScript code. To run the code, use the node command:

node todo.js

You should see the output of the initial To-Do list, the list after marking an item as completed, and the list after sorting by priority.

clean code in TypeScript
the output of the initial To-Do list

Conclusion

By following these recommended techniques and applying them in real-world situations, like the To-Do application we made, you can greatly enhance the clarity, adaptability, and maintainability of your TypeScript code.

As you progress in your use of TypeScript, keep exploring the language’s advanced features and design patterns. Doing so will enable you to create more intricate and efficient applications while maintaining a well-organized and professional codebase.

Always keep in mind that writing clean code is a continuous process that requires ongoing learning, adjustment, and improvement.