20 TypeScript Tricks Every Developer Should Know 🚀
Jagroop Singh

Jagroop Singh @jagroop2001

About: 👨‍💻 Full Stack Developer | 🤖 Machine Learning Developer | 🤝 Dev Relations Pro – 💼 Available for Hire | 24k+ Followers | 355k+ Views

Location:
India
Joined:
Apr 5, 2022

20 TypeScript Tricks Every Developer Should Know 🚀

Publish Date: Oct 23 '24
201 18

TypeScript is a powerhouse for modern JavaScript development, bringing type safety and advanced features to the table.
While many developers know the basics, there are hidden gems and practical tricks that can make your code more efficient, clean, and maintainable.
Let’s dive into 20 TypeScript tricks that every developer should know, with examples and practical approaches! 💻


1. Non-Nullable Types (NonNullable)

TypeScript provides the NonNullable utility to eliminate null and undefined from a type. This can help you avoid unexpected null values.

type User = { name: string; age?: number | null };
const user: NonNullable<User["age"]> = 30; // ✅ No null or undefined allowed
Enter fullscreen mode Exit fullscreen mode

2. Using Partial for Flexibility 🔧

The Partial<T> utility makes all properties in a type optional, which is great when you're updating only a subset of object fields.

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

const updateUser = (user: Partial<User>) => {
  // You can pass only the fields you want to update
  return { ...user, updatedAt: new Date() };
};

updateUser({ name: 'John' }); // No need to provide the entire object
Enter fullscreen mode Exit fullscreen mode

3. Leverage Readonly for Immutable Data 🔒

When you need immutability in TypeScript, Readonly<T> makes all properties of a type immutable, preventing reassignment.

const config: Readonly<{ apiUrl: string; retries: number }> = {
  apiUrl: 'https://api.example.com',
  retries: 5
};

config.apiUrl = 'https://newapi.com'; // ❌ Error: Cannot assign to 'apiUrl' because it is a read-only property
Enter fullscreen mode Exit fullscreen mode

4. Mapped Types for Dynamic Property Typing 🛠️

Mapped types let you create new types by transforming existing ones. This is handy for creating variations of an object type.

type Status = 'loading' | 'success' | 'error';
type ApiResponse<T> = {
  [K in Status]: T;
};

const response: ApiResponse<string> = {
  loading: 'Fetching...',
  success: 'Data loaded',
  error: 'Something went wrong'
};
Enter fullscreen mode Exit fullscreen mode

5. Tuple Types with Optional Elements 🔢

Did you know TypeScript allows optional elements in tuples? This is great when dealing with variadic function arguments.

type UserTuple = [string, number?, boolean?];

const user1: UserTuple = ['Alice'];          // ✅ Just the name
const user2: UserTuple = ['Bob', 30];        // ✅ Name and age
const user3: UserTuple = ['Charlie', 25, true]; // ✅ Full tuple
Enter fullscreen mode Exit fullscreen mode

6. Union Types with Exhaustive Checks 🔍

Ensure you're handling all possible cases with union types and exhaustive checks in switch statements.

type Status = 'open' | 'closed' | 'pending';

function handleStatus(status: Status) {
  switch (status) {
    case 'open':
      return 'Opened';
    case 'closed':
      return 'Closed';
    case 'pending':
      return 'Pending';
    default:
      const exhaustiveCheck: never = status; // ❌ Error if a new status type is added but not handled
      return exhaustiveCheck;
  }
}
Enter fullscreen mode Exit fullscreen mode

7. Utility Type Omit for Excluding Keys 🗑️

Sometimes you need to create an object type that excludes certain keys. Omit is your friend here!

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Omit<Todo, 'description'>;

const todo: TodoPreview = {
  title: 'Learn TypeScript',
  completed: false
};
Enter fullscreen mode Exit fullscreen mode

8. Type Narrowing with in and instanceof 🕵️

Use in and instanceof to narrow down types at runtime.

function processInput(input: string | number | { title: string }) {
  if (typeof input === 'string') {
    return input.toUpperCase(); // Narrowed to string
  } else if (typeof input === 'number') {
    return input * 2; // Narrowed to number
  } else if ('title' in input) {
    return input.title; // Narrowed to object with title property
  }
}
Enter fullscreen mode Exit fullscreen mode

9. Conditional Types for Advanced Type Logic 🔄

Conditional types give you incredible flexibility for transforming types based on conditions.

type IsString<T> = T extends string ? true : false;

type CheckString = IsString<'Hello'>; // true
type CheckNumber = IsString<42>; // false
Enter fullscreen mode Exit fullscreen mode

10. Use as const for Immutable Literal Types 📜

as const is great for freezing values and ensuring that TypeScript treats them as literal types, not mutable values.

const COLORS = ['red', 'green', 'blue'] as const;

type Color = typeof COLORS[number]; // 'red' | 'green' | 'blue'
Enter fullscreen mode Exit fullscreen mode

Follow me on github:

Jagroop2001 (Jagroop) · GitHub

👨‍💻 Full Stack Developer | 🤖 Machine Learning Developer | 🤝 Dev Relations Pro – 💼 Available for Hire - Jagroop2001

favicon github.com

11. Extract and Exclude to Refine Types 🧹

Use Extract and Exclude to filter out or pick specific types from a union.

type T = 'a' | 'b' | 'c';
type OnlyAOrB = Extract<T, 'a' | 'b'>; // 'a' | 'b'
type ExcludeC = Exclude<T, 'c'>; // 'a' | 'b'
Enter fullscreen mode Exit fullscreen mode

12. Type Guards for Custom Validation

Create your own type guards to refine types dynamically at runtime.

function isString(input: any): input is string {
  return typeof input === 'string';
}

const value: any = 'Hello';

if (isString(value)) {
  console.log(value.toUpperCase()); // Safe: value is a string here
}
Enter fullscreen mode Exit fullscreen mode

13. Use Record for Dynamic Object Types 📋

When you need a type for an object with dynamic keys, Record<K, V> is the perfect fit.

type Role = 'admin' | 'user' | 'guest';
const permissions: Record<Role, string[]> = {
  admin: ['read', 'write', 'delete'],
  user: ['read', 'write'],
  guest: ['read']
};
Enter fullscreen mode Exit fullscreen mode

14. Dynamic Class Properties with Index Signatures 🏗️

Index signatures allow you to create objects or classes with dynamically named properties.

class DynamicObject {
  [key: string]: any;
}

const obj = new DynamicObject();
obj.name = 'Alice';
obj.age = 30;
Enter fullscreen mode Exit fullscreen mode

15. never Type for Impossible States 🚫

The never type represents values that should never occur. It's commonly used in exhaustive checks.

function assertNever(value: never): never {
  throw new Error(`Unexpected value: ${value}`);
}
Enter fullscreen mode Exit fullscreen mode

16. Optional Chaining for Safe Property Access 🔗

Use optional chaining (?.) to safely access deeply nested properties without worrying about undefined errors.

const user = { profile: { name: 'John' } };
const userName = user?.profile?.name; // 'John'
const age = user?.profile?.age ?? 'Not provided'; // Fallback to default
Enter fullscreen mode Exit fullscreen mode

17. Default Values with Nullish Coalescing (??) 🔄

Use the nullish coalescing operator to provide a fallback value only if the original value is null or undefined.

const input: string | null = null;
const defaultValue = input ?? 'Default'; // 'Default'
Enter fullscreen mode Exit fullscreen mode

18. Inferring Return Types with ReturnType 🔄

The ReturnType<T> utility extracts the return type of a function, which can be helpful when you're dealing with complex types.

function getUser() {
  return { name: 'John', age: 30 };
}

type UserReturn = ReturnType<typeof getUser>; // { name: string; age: number; }
Enter fullscreen mode Exit fullscreen mode

19. Type Parameters in Functions 🧑‍💻

Generic type parameters make your functions flexible and reusable across different types.

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

identity<string>('Hello'); // 'Hello'
identity<number>(42); // 42
Enter fullscreen mode Exit fullscreen mode

20. Intersection Types to Combine Structures

Intersection types let you combine multiple types into one.

type Admin = { privileges: string[] };
type User = { name: string };

type AdminUser = Admin & User;

const adminUser: AdminUser = {
  privileges: ['admin', 'editor'],
  name: 'Alice'
};
Enter fullscreen mode Exit fullscreen mode

These tricks will help you take your TypeScript skills to the next level! 🔥 Keep experimenting and integrating these patterns into your projects for cleaner, more efficient code. Happy coding! 😎👨‍💻

Comments 18 total

  • john
    johnOct 24, 2024

    Thanks for sharing !!
    11. Extract and Exclude to Refine Types is very new to me and become my personal favorite.

  • sewiko
    sewikoOct 24, 2024

    I just discovered the Partial<T> utility, and I think it’s an impressive implementation. Instead of relying on a any keyword, it provides a clean and flexible way to create objects with optional properties.
    Thanks for sharing.

  • Zizaco
    ZizacoOct 26, 2024
    1. Union Types with Exhaustive Checks is gold!
  • Colin Kierans
    Colin KieransOct 26, 2024

    Thanks for these! Extract has been useful at my job for dealing with unions that are referenced in other semi related types.

  • Boby Tiwari
    Boby TiwariOct 27, 2024

    💯

  • Peter Vivo
    Peter VivoOct 27, 2024

    Great collection, thx!

    One more trick :: in my opinion jsDoc is better than Typescript

  • sebkolind
    sebkolindOct 27, 2024

    Great post! There are definitely some useful tips and tricks included!

  • Nozibul Islam
    Nozibul IslamOct 29, 2024

    I'm glad you found the TS shortcuts helpful! If you need more specific shortcuts or tricks, especially for any particular task in TS or a particular framework, feel free to ask. It’s a pleasure to share helpful insights!

  • Web
    WebOct 30, 2024

    nice posts about typescript !!

Add comment