WHAT'S NEW
TypeScript · Lesson 09 of 11

Unlocking Generics

Write flexible functions, interfaces, and classes that work with multiple types while keeping TypeScript’s powerful type checking.

Type Parameters Generic Functions Generic Interfaces Constraints
● ● ●   generics.ts
function identity<T>(value: T): T {
  return value;
}

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

console.log(result);
CONSOLE OUTPUT
> Ready to run…
01 / What Are Generics?

Write Code That Works with Multiple Types

Imagine writing a function that returns whatever value you give it. You could make separate versions for strings, numbers, and other data types. But what if you could write that function just once?

Generics let you create reusable code while preserving information about the types being used.

first-generic.ts
function identity<T>(value: T): T {
  return value;
}

console.log(identity<string>("Moe"));
console.log(identity<number>(9));
Console Output:
Moe 9

The T is a type parameter. It acts as a placeholder for a type that will be supplied when the function is used.

Remember: Generics let one implementation work with different types without throwing away useful type information.
02 / Why Not Just Use Any?

Keep the Type Information You Need

You might wonder why we don’t simply use any. The difference is that any disables important type checks, while a generic preserves the relationship between inputs and outputs.

generic-vs-any.ts
function unsafe(value: any): any {
  return value;
}

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

const first = unsafe("Moe");
const second = safe("Moe");

// second is known to be a string.
// first has type any.

With the generic version, TypeScript knows that the returned value has the same type as the input.

03 / Generic Type Inference

Let TypeScript Figure Out the Type

Most of the time, you don’t need to write the type argument explicitly. TypeScript can infer it from the value passed into the function.

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

const developer = identity("Moe");
const level = identity(9);
const completed = identity(true);

console.log(developer);
console.log(level);
console.log(completed);
Console Output:
Moe 9 true

TypeScript infers string, number, and boolean types from the three arguments. You still get type safety without repeatedly writing type annotations.

04 / Generic Arrays

Use Generics with Collections

You’ve already used generic types without necessarily realizing it. Array<T> is the generic form of an array type.

generic-arrays.ts
const skills: Array<string> = [
  "HTML",
  "CSS",
  "TypeScript"
];

const levels: Array<number> = [1, 5, 9];

console.log(skills[2]);
console.log(levels[1]);
Console Output:
TypeScript 5

Array<string> and string[] mean the same thing. Both describe arrays whose elements are strings.

05 / Generic Functions with Arrays

Create Reusable Collection Helpers

Generics become especially useful when building functions that operate on arrays.

first-item.ts
function firstItem<T>(items: T[]): T | undefined {
  return items[0];
}

const firstSkill = firstItem(["HTML", "CSS"]);
const firstLevel = firstItem([3, 6, 9]);

console.log(firstSkill);
console.log(firstLevel);
Console Output:
HTML 3

The same function works with different array element types. We include undefined in the return type because an empty array has no first element.

06 / Multiple Type Parameters

Work with Two Types at Once

A generic function can accept more than one type parameter. Common names include T, U, and K, but you can use descriptive names too.

two-types.ts
function makePair<T, U>(
  first: T,
  second: U
): [T, U] {
  return [first, second];
}

const pair = makePair("Moe", 9);

console.log(pair[0]);
console.log(pair[1]);
Console Output:
Moe 9

Here, TypeScript remembers that the first item is a string and the second is a number. The return type is a tuple containing those two types in order.

07 / Generic Interfaces

Build Flexible Data Contracts

Interfaces can accept type parameters, too. This makes them useful for reusable response objects, records, and application data structures.

generic-interface.ts
interface Result<T> {
  success: boolean;
  data: T;
}

const developerResult: Result<string> = {
  success: true,
  data: "Moe"
};

const levelResult: Result<number> = {
  success: true,
  data: 9
};

console.log(developerResult.data);
console.log(levelResult.data);
Console Output:
Moe 9

The same Result<T> interface can describe responses containing different types of data.

08 / Generic Classes

Create Reusable Object Containers

Classes can also be generic. Imagine a container that holds one value, regardless of whether it’s a string, number, or another type.

generic-class.ts
class Box<T> {
  constructor(public value: T) {}

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

const nameBox = new Box<string>("Moe");
const levelBox = new Box<number>(9);

console.log(nameBox.getValue());
console.log(levelBox.getValue());
Console Output:
Moe 9

Each instance keeps its own type information. You can reuse the class while still getting accurate type checking.

09 / Generic Constraints

Require Specific Capabilities

Sometimes your generic function needs to use a particular property. A constraint tells TypeScript what capabilities the type argument must have.

constraints.ts
interface Named {
  name: string;
}

function introduce<T extends Named>(
  value: T
): void {
  console.log("Hello, " + value.name);
}

introduce({
  name: "Moe",
  language: "TypeScript"
});

// TypeScript error:
// introduce({ level: 9 });
Console Output:
Hello, Moe

The constraint T extends Named requires an object with a string name property. It can still contain additional properties.

Important: In a generic constraint, extends means the type must satisfy the required structure. It doesn’t necessarily mean class inheritance.
10 / Putting It Together

Create a Reusable Learning Record

Let’s combine generic interfaces, functions, and classes into a small program for tracking developer information.

learning-record.ts
interface LearningRecord<T> {
  title: string;
  value: T;
}

function displayRecord<T>(
  record: LearningRecord<T>
): void {
  console.log(record.title + ": " + record.value);
}

const developer: LearningRecord<string> = {
  title: "Developer",
  value: "Moe"
};

const level: LearningRecord<number> = {
  title: "Level",
  value: 9
};

displayRecord(developer);
displayRecord(level);
Console Output:
Developer: Moe Level: 9

One interface and one function handle multiple kinds of records while preserving the correct value types.

🚀 mC0de Challenge

Build a Generic Learning Tracker

Create a reusable tracker that can store different kinds of developer progress data.

Your Mission

  1. Create a generic interface named LearningRecord<T>.
  2. Give it a title property of type string and a value property of type T.
  3. Create a generic function named displayRecord<T>() that prints a record.
  4. Create a record for Moe’s name using string.
  5. Create another record for the current lesson number using number.
  6. Display both records in the console.
  7. Try assigning a string to a LearningRecord<number> value and observe the type error.
Extra challenge: Create a LearningRecord<boolean> to track whether a lesson is complete.
💻 Interactive TypeScript Sandbox

Build Your Generic Learning Tracker!

Edit your records, experiment with type parameters, and run the program. TypeScript checks your code before the compiled JavaScript runs.

Console Output
Lesson 09 Recap

What You’ve Learned

You’re Writing Flexible, Type-Safe Code!

You can now:

  • Explain how generics preserve type information.
  • Create generic functions with <T>.
  • Use TypeScript’s generic type inference.
  • Recognize Array<T> as a generic type.
  • Write generic functions that work with arrays.
  • Use multiple type parameters.
  • Create generic interfaces and classes.
  • Apply generic constraints using extends.

Next up: Union Types & Type Guards — learn how to work safely with values that can have more than one type!

Your Learning Journey

0 / 11 Completed

Nine lessons in! You’re approaching the finish line.

🎉

You’re Writing Smarter TypeScript!

You’ve learned how generics make functions, interfaces, and classes reusable without sacrificing type safety. You’re now ready to handle more complex data structures and application logic!

PIX0
</> {} JS 01
0101 </> CSS JS HTML PIX0 const {} 1010 async mC0de function return 0101 <div> 1010 PIX0 CSS JS HTML const {} 0101 mC0de
PIX0 // WORLD OF CODE
PIX0 IS PREPARING SOMETHING AWESOME FOR YOU 0%