

0 / 2 embers
0 / 3000 xp
click for more info
Complete a lesson to start your streak
click for more info
Still calibrating
click for more info
Not enough gems
Cost: 6 gems
1: Generics
incomplete
2: Multiple Type Parameters
incomplete
3: Generic Constraints
incomplete
4: Type Parameters for Types
incomplete
5: Generic Type Inference
incomplete
6: Generic Classes
incomplete
Back
ctrl+,
Next
ctrl+.
This lesson's interactive features are locked, please to keep using them
We're kinda beating a dead horse at this point.
Look, we get it, you can add type parameters to almost anything in TypeScript...
So yeah, classes can be generic too. To keep it somewhat interesting, let's combine a few concepts:
InMemoryRepository is a generic classRepository<T>)T is constrained to have an id propertyinterface Repository<T> {
getAll(): T[];
getById(id: string): T | undefined;
save(item: T): void;
}
class InMemoryRepository<T extends { id: string }> implements Repository<T> {
private items: T[] = [];
getAll(): T[] {
return [...this.items];
}
getById(id: string): T | undefined {
return this.items.find((item) => item.id === id);
}
save(item: T): void {
const index = this.items.findIndex((i) => i.id === item.id);
if (index >= 0) {
this.items[index] = item;
} else {
this.items.push(item);
}
}
}
There are a few things to note about the example above:
implements keyword is to ensure that the class adheres to the Repository<T> interface - TypeScript will yell at us if our InMemoryRepository class can't be used as a Repository<T>.Repository<T> doesn't need an id property, our InMemoryRepository does.InMemoryRepository can be used to hold any type of object, as long as it has an id property. And all the implementation logic is shared between all the different possible types.Let's create an InMemoryRepository for Shinigami:
interface Shinigami {
id: string;
name: string;
}
const deathNoteRepo = new InMemoryRepository<Shinigami>();
deathNoteRepo.save({ id: "1", name: "Ryuk" });
deathNoteRepo.save({ id: "2", name: "Rem" });
console.log(deathNoteRepo.getAll());
Of course, if we try to create an InMemoryRepository for something that doesn't have an id property, TypeScript will yell at us:
interface Psychopaths {
name: "Light Yagami" | "L";
}
// Error: Type 'Psychopaths' does not satisfy the constraint '{ id: string; }'
const psychopathRepo = new InMemoryRepository<Psychopaths>();
Support.ai is launching experiments again. You've been asked to implement a class that manages feature flags by name. At Support.ai we commit to features. Once a flag is enabled, there's no going back.
Complete the FeatureFlag class. It should keep track of all the enabled flags, and allow users of the class to enable new flags and check if a flag is enabled.
A flag is enabled if it simply exists in the #flags set.
Notice that in the test suite, the extends string syntax allows the user of the class to be more specific than just "feature flags are strings". Look at the new FeatureFlag<BG3Flag>(); line. It's dynamically constraining the types of possible flags to a union by using a type parameter!
Sets in JavaScript have a few useful methods:
add(value): Adds a new element to the set.has(value): Checks if an element exists in the set.