Home Coding & Scripting Understanding Generics and Utility Types in TypeScript

Understanding Generics and Utility Types in TypeScript

242
0

When you start working with TypeScript, you’ll soon come across Generics and Utility Types.
They may sound complicated, but they’re actually tools that make your code more reusable, flexible, and type-safe.

Let’s break them down step-by-step with examples that are easy to follow.


1. What Are Generics?

Imagine you’re writing a function that takes a value and returns it — kind of like an “identity” function.
Without generics, you’d have to write the same function multiple times for different types:

function returnNumber(value: number): number {
  return value;
}

function returnString(value: string): string {
  return value;
}

That’s a lot of repetition!
Instead, we can use generics to make this function reusable for any type.

Example:

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

Now you can use it for any type:

identity<number>(100);     // returns number
identity<string>("Hello"); // returns string

Here, T is a type variable — a placeholder for any type.
When you call the function, TypeScript replaces T with the actual type you pass in.


2. Generics with Arrays and Interfaces

Generics are not just for functions — you can use them in interfaces, classes, and types too.

Example: Generic Array

function getFirst<T>(arr: T[]): T {
  return arr[0];
}

const numbers = getFirst([10, 20, 30]); // number
const names = getFirst(["Heartin", "Sneha"]); // string

Example: Generic Interface

interface Box<T> {
  content: T;
}

const numberBox: Box<number> = { content: 42 };
const stringBox: Box<string> = { content: "Hello" };

The benefit?
You don’t need separate interfaces for NumberBox, StringBox, etc. — one generic interface covers all.


3. Generic Constraints

Sometimes, you want to make sure your generic type has certain properties.
For example, you might only want to work with objects that have a length property (like arrays or strings).

You can do that using constraints.

Example:

function logLength<T extends { length: number }>(item: T): void {
  console.log(item.length);
}

logLength("Hello"); // works
logLength([1, 2, 3]); // works
// logLength(10); ❌ Error: number doesn’t have 'length'

The keyword extends restricts T to types that have a length property.


4. What Are Utility Types?

TypeScript includes several built-in utility types that make working with objects and types easier.
They save you from writing repetitive or complex type definitions.

Let’s look at the most common ones.


🔹 Partial<Type>

Makes all properties optional.

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

const updateUser = (user: Partial<User>) => {
  console.log(user);
};

updateUser({ name: "Heartin" }); // ✅ age is optional now

🔹 Required<Type>

Makes all properties mandatory.

interface User {
  name?: string;
  age?: number;
}

const user: Required<User> = { name: "Sneha", age: 35 }; // both required

🔹 Readonly<Type>

Prevents properties from being changed.

interface Config {
  appName: string;
}

const config: Readonly<Config> = { appName: "BuddyTutor" };

// config.appName = "NewApp"; ❌ Error: Cannot assign to read-only property

🔹 Pick<Type, Keys>

Selects specific properties from a type.

interface User {
  name: string;
  age: number;
  email: string;
}

type BasicUser = Pick<User, "name" | "email">;

const user: BasicUser = { name: "Heartin", email: "heartin@example.com" };

🔹 Omit<Type, Keys>

Does the opposite of Pick — removes specific properties.

type UserWithoutEmail = Omit<User, "email">;

const user: UserWithoutEmail = { name: "Sneha", age: 35 };

🔹 Record<Keys, Type>

Creates an object type where all keys have the same value type.

type Roles = "admin" | "editor" | "viewer";

const permissions: Record<Roles, boolean> = {
  admin: true,
  editor: true,
  viewer: false
};

🔹 ReturnType<Type>

Gets the return type of a function.

function getAge() {
  return 35;
}

type AgeType = ReturnType<typeof getAge>; // number

5. Combining Generics with Utility Types

You can mix generics and utility types to create powerful and reusable code.

Example:

function updateRecord<T extends object>(record: T, fields: Partial<T>): T {
  return { ...record, ...fields };
}

const user = { name: "Heartin", age: 35 };
const updatedUser = updateRecord(user, { age: 36 });

console.log(updatedUser); // { name: "Heartin", age: 36 }

Here:

  • T can be any object.
  • Partial<T> lets you update only some fields.

6. Quick Summary

ConceptWhat It DoesExample
GenericCreates reusable code that works with any typefunction identity<T>(value: T): T
PartialMakes all fields optionalPartial<User>
PickKeeps only selected fieldsPick<User, "name">
OmitRemoves selected fieldsOmit<User, "email">
ReadonlyPrevents modificationReadonly<User>
RecordCreates a map-like objectRecord<string, number>
ReturnTypeExtracts return type of functionReturnType<typeof fn>

Final Thoughts

Generics and utility types make your TypeScript code cleaner, smarter, and reusable.
Generics help your code work with multiple types, while utility types help you transform and manage complex objects easily.

If you’re building large applications, mastering these two features will make your code more maintainable and type-safe.

Previous articleUnderstanding Types, Interfaces, and Type Aliases in TypeScript
Next articleUnderstanding Type Inference and Type Assertion in TypeScript
Heartin Kanikathottu
As a seasoned Cloud and Security Architect, I’ve led transformative initiatives in key roles, including Vice President at Morgan Stanley, Principal Architect at Societe Generale, and Tech Lead & Cloud Security Architect at VMware, among others. I’m also an internationally published author with multiple books available on platforms like Amazon and O'Reilly. Notably, one of my books was recognized as the 8th best cloud computing book of all time in 2020, reflecting the impact of my contributions to the field. With over 15 professional certifications from providers such as Microsoft (Azure), Amazon (AWS), Oracle (Java), Pivotal (Spring), and IBM, I bring a wealth of expertise to my work. Academically, I hold dual Master’s degrees in Cloud Computing and Data Analytics. I’m passionate about sharing knowledge and mentoring others, which is why I actively speak at global technical forums such as Tech Opportunities Fest at Platform Calgary, Google's Kubernetes Meetup, Java User Group, Elasticsearch Meetup, and the Agile India Conference.

LEAVE A REPLY

Please enter your comment!
Please enter your name here