Lesson 43 of 60 – Generics
72%

Generics in TypeScript

Generics allow you to write reusable and type-safe code that can work with different types. Instead of specifying one fixed type, you can create a reusable component that works with many types.

Note: Generics are commonly used with functions, arrays, interfaces, classes, and reusable components.

1. What are Generics?

Generics allow a function, class, or interface to work with different data types while maintaining type safety.

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

Here, T represents a type that will be determined when the function is used.

2. Why Use Generics?

Generics help you create reusable code without losing type information.

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

const name = identity("Amit");
const age = identity(25);

The same function can work with both strings and numbers.

3. Generic Type Parameter

The type parameter is written inside angle brackets.

function display<T>(
    value: T
): T {

    return value;
}

T is a type parameter. You can choose another meaningful name, but T is commonly used.

4. Generic Function with String

function identity<T>(
    value: T
): T {

    return value;
}

const result =
    identity<string>("Hello");

console.log(result);

Here, T becomes string.

5. Generic Function with Number

function identity<T>(
    value: T
): T {

    return value;
}

const result =
    identity<number>(100);

console.log(result);

In this example, T becomes number.

6. TypeScript Can Infer Generic Types

You often do not need to explicitly provide the generic type.

function identity<T>(
    value: T
): T {

    return value;
}

const name =
    identity("Rahul");

const age =
    identity(30);

TypeScript can infer the type from the argument.

7. Generic Array Function

Generics can be used to create functions that work with arrays of different types.

function getFirst<T>(
    items: T[]
): T {

    return items[0];
}

const number =
    getFirst([10, 20, 30]);

const name =
    getFirst(["Amit", "Rahul"]);

8. Generic Array Example

function printItems<T>(
    items: T[]
): void {

    items.forEach(item => {
        console.log(item);
    });

}

printItems<number>(
    [10, 20, 30]
);

printItems<string>(
    ["HTML", "CSS", "JavaScript"]
);

9. Generic Function with Two Parameters

A generic function can contain more than one parameter.

function pair<T>(
    first: T,
    second: T
): T[] {

    return [first, second];

}

const numbers =
    pair(10, 20);

const names =
    pair("Amit", "Rahul");

10. Multiple Generic Types

You can use multiple type parameters.

function createPair<T, U>(
    first: T,
    second: U
) {

    return {
        first,
        second
    };

}

const result =
    createPair("Amit", 25);

Here, T and U can represent different types.

11. Generic Object

function createObject<T>(
    value: T
) {

    return {
        value
    };

}

const result =
    createObject("TypeScript");

console.log(result.value);

The object preserves the type of the supplied value.

12. Generic Type Alias

A type alias can also contain a generic type parameter.

type Box<T> = {
    value: T;
};

const numberBox: Box<number> = {
    value: 100
};

const stringBox: Box<string> = {
    value: "Hello"
};

13. Generic Interface

Interfaces can also be made generic.

interface Box<T> {
    value: T;
}

const numberBox: Box<number> = {
    value: 100
};

const stringBox: Box<string> = {
    value: "Hello"
};

14. Generic Class

Classes can use generic type parameters.

class Box<T> {

    value: T;

    constructor(value: T) {
        this.value = value;
    }

    getValue(): T {
        return this.value;
    }

}

const numberBox =
    new Box<number>(100);

console.log(
    numberBox.getValue()
);

15. Generic Class with String

class Box<T> {

    constructor(
        public value: T
    ) {}

    getValue(): T {
        return this.value;
    }

}

const box =
    new Box<string>("Hello");

console.log(
    box.getValue()
);

The same class can work with different types.

16. Generic Constraints

Sometimes a generic type needs to satisfy a specific requirement. You can use extends to create a constraint.

function getLength<T extends { length: number }>(
    value: T
): number {

    return value.length;
}

console.log(
    getLength("Hello")
);

console.log(
    getLength([1, 2, 3])
);

17. Why Use Generic Constraints?

A constraint allows the generic function to safely use properties that are guaranteed to exist.

function getLength<
    T extends { length: number }
>(value: T): number {

    return value.length;
}

TypeScript knows that value has a length property.

18. Generic Constraints with Interfaces

interface HasId {
    id: number;
}

function showId<T extends HasId>(
    item: T
) {

    console.log(item.id);

}

showId({
    id: 101,
    name: "Amit"
});

The generic type must contain an id property of type number.

19. keyof with Generics

The keyof operator can be used with generics to work with the keys of an object.

function getProperty<
    T,
    K extends keyof T
>(
    object: T,
    key: K
): T[K] {

    return object[key];
}

const student = {
    name: "Amit",
    age: 20
};

const name =
    getProperty(student, "name");

const age =
    getProperty(student, "age");

20. Generic Function with Objects

function merge<T, U>(
    first: T,
    second: U
): T & U {

    return {
        ...first,
        ...second
    };

}

const result =
    merge(
        { name: "Amit" },
        { age: 20 }
    );

console.log(result);

Multiple generic types can be combined to create reusable object operations.

21. Generic Promise

Generics are commonly used with promises to describe the type of data that will be returned.

function getStudent(): Promise<string> {

    return Promise.resolve(
        "Amit"
    );

}

Here, the promise resolves to a string value.

22. Generic API Response

interface ApiResponse<T> {
    data: T;
    success: boolean;
}

const response:
    ApiResponse<string> = {

    data: "Student loaded",
    success: true

};

The same API response structure can work with different data types.

23. Generic API Response with Student

interface Student {
    id: number;
    name: string;
}

interface ApiResponse<T> {
    data: T;
    success: boolean;
}

const response:
    ApiResponse<Student> = {

    data: {
        id: 101,
        name: "Amit"
    },

    success: true

};

console.log(
    response.data.name
);

24. Generics in React Components

Generics can make React components reusable with different types of data.

type ListProps<T> = {
    items: T[];
};

function List<T>(
    { items }: ListProps<T>
) {

    return (
        <div>
            {items.map((item, index) => (
                <div key={index}>
                    {String(item)}
                </div>
            ))}
        </div>
    );

}

25. Generics in React Native

Generics are especially useful when building reusable React Native components that display different types of data.

type ListProps<T> = {
    items: T[];
    renderItem: (item: T) => string;
};

function DataList<T>(
    { items, renderItem }: ListProps<T>
) {

    return items.map(
        item => renderItem(item)
    );

}

The component can work with different data types while maintaining type safety.

26. Generic Default Type

A generic type parameter can have a default type.

interface Response<
    T = string
> {

    data: T;

}

const response:
    Response = {

    data: "Hello"

};

const numberResponse:
    Response<number> = {

    data: 100

};

27. Generic Utility Function

Generics are useful for creating small reusable utility functions.

function reverseArray<T>(
    items: T[]
): T[] {

    return [...items].reverse();

}

const numbers =
    reverseArray([1, 2, 3]);

const names =
    reverseArray([
        "Amit",
        "Rahul",
        "Neha"
    ]);

console.log(numbers);
console.log(names);

28. Common Generic Mistakes

  • Using any instead of a useful generic type.
  • Using a generic when a simple type is sufficient.
  • Forgetting to add constraints when a property is required.
  • Using incorrect generic type parameters.
  • Making generic code unnecessarily complicated.
  • Not preserving type information in reusable functions.

29. Complete Practical Example

interface Student {
    id: number;
    name: string;
}

interface ApiResponse<T> {
    data: T;
    success: boolean;
}

function createResponse<T>(
    data: T
): ApiResponse<T> {

    return {
        data,
        success: true
    };

}

const student: Student = {
    id: 101,
    name: "Amit"
};

const response =
    createResponse(student);

console.log(
    response.data.name
);

console.log(
    response.success
);

This example uses a generic function and a generic interface to create a reusable and type-safe API response structure.

30. Generics Summary

Generics are one of the most important features of TypeScript for creating reusable and type-safe code.

function identity<T>(
    value: T
): T {

    return value;
}

const name =
    identity("TypeScript");

const age =
    identity(25);

Generics can be used with functions, arrays, interfaces, classes, API responses, React components, and React Native components.

📌 Key Points

  • Generics create reusable and type-safe code.
  • Generic type parameters are commonly written as T.
  • TypeScript can often infer generic types automatically.
  • Generics can be used with functions.
  • Generics can be used with arrays.
  • Generics can be used with interfaces and type aliases.
  • Generics can be used with classes.
  • Multiple generic type parameters can be used.
  • Generic constraints can be created using extends.
  • keyof can be combined with generics.
  • Generics are useful for API response types.
  • Generics are useful for reusable React and React Native components.
  • Default generic types can be specified.
  • Generics help preserve type information across reusable code.

🧠 Quick Quiz

Question: Which symbol is commonly used to define a generic type parameter in TypeScript?