codez.guru

Why Use Constraints?

Without constraints, generic types can be anything β€” even things that don’t support the operations you want.

function getLength<t>(value: T): number {
  return value.length; // ❌ Error: T might not have `.length`
}

We can fix this with a constraint.


Using extends to Restrict Generics

You can require that a generic extends a base type:

function getLength<t extends length: number>(value: T): number {
  return value.length;
}

Now the function works only with types that have a .length property:

getLength("hello");     // βœ…
getLength([1, 2, 3]);    // βœ…
getLength(123);          // ❌ Error

Accessing Object Properties Safely

You can ensure that a key exists on an object using keyof:

function getProp<t k extends keyof t>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { name: "Lior", age: 30 };

getProp(user, "name"); // "Lior"
getProp(user, "email"); // ❌ Error: Property does not exist

This makes your generic functions type-safe and self-validating.


Default Type Parameters

You can provide a default if the caller doesn’t supply one:

type ApiResponse<t any> = {
  data: T;
  error?: string;
};

const res: ApiResponse = {
  data: "fallback type is any",
};

This is useful for utilities or when you want to provide a sensible fallback.


Combining Constraints and Defaults

You can use both together:

function identity<t extends string number="string">(value: T): T {
  return value;
}

identity("test"); // ok
identity(123);    // ok
identity(true);   // ❌ Error: boolean not allowed

Summary

  • Use extends to restrict generics to specific shapes or types
  • Use keyof and indexed types to safely access object properties
  • Provide default types for simpler usage and better fallback
  • Combining constraints and defaults gives you control and flexibility

Next up: Lesson 13 – Utility Types: Partial, Pick, Omit, and Record β€” learn how to transform types like a pro.