Bird
Raised Fist0
Typescriptprogramming~10 mins

Generic repository pattern in Typescript - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Generic repository pattern
Define Generic Interface Repository<T>
↓
Implement Repository Class<T>
↓
Create Specific Repository Instance
↓
Use Repository Methods (add, get)
↓
Manage Data of Type T
↓
End
The generic repository pattern defines a reusable interface and class to manage data of any type T, allowing creation and use of specific repositories for different data types.
Execution Sample
Typescript
interface Repository<T> {
  add(item: T): void;
  get(id: number): T | undefined;
}

class RepositoryImpl<T> implements Repository<T> {
  private items = new Map<number, T>();
  add(item: T) { this.items.set(this.items.size + 1, item); }
  get(id: number) { return this.items.get(id); }
}

const userRepo = new RepositoryImpl<{name: string}>();
userRepo.add({name: "Alice"});
const user = userRepo.get(1);
This code defines a generic repository interface and class, then creates a user repository to add and get a user object.
Execution Table
StepActionVariable/MethodInputState ChangeOutput
1Define interface Repository<T>Repository<T>N/AInterface readyN/A
2Implement class RepositoryImpl<T>RepositoryImpl<T>N/AClass ready with empty items MapN/A
3Create userRepo instanceuserRepoN/AuserRepo.items = empty MapN/A
4Call userRepo.addadd{name: "Alice"}items Map size=1, key=1, value={name: "Alice"}void
5Call userRepo.getget1No change{name: "Alice"}
6EndN/AN/AFinal items Map has 1 entryN/A
💡 No more actions, repository used to add and get data successfully
Variable Tracker
VariableStartAfter Step 3After Step 4After Step 5Final
userRepo.itemsundefinedempty Map {}Map {1 => {name: "Alice"}}Map {1 => {name: "Alice"}}Map {1 => {name: "Alice"}}
userundefinedundefinedundefined{name: "Alice"}{name: "Alice"}
Key Moments - 3 Insights
Why does userRepo.items start empty and then have one entry after add?
At step 3, userRepo is created with an empty Map. At step 4, add inserts the item with key 1, so the Map size becomes 1 as shown in execution_table row 4.
What type does the repository store and return?
The repository stores and returns objects of type T, which in this example is {name: string}, as seen in the generic interface and class and the userRepo instance.
Why does get return undefined if the id is not found?
The get method returns undefined if the Map has no entry for the given id, which matches the Map.get behavior and is shown in the method signature Repository<T>.get.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table at step 4. What is the size of userRepo.items after adding the first item?
A1
B0
C2
Dundefined
💡 Hint
Check the 'State Change' column at step 4 in the execution_table where items Map size is updated.
At which step does the variable 'user' get assigned a value?
AStep 3
BStep 5
CStep 4
DStep 6
💡 Hint
Look at the variable_tracker for 'user' and see when it changes from undefined to an object.
If we call userRepo.get(2) instead of get(1), what would be the output?
AError
B{name: "Alice"}
Cundefined
Dnull
💡 Hint
Refer to the get method behavior and Map.get when key is missing, as explained in key_moments.
Concept Snapshot
Generic Repository Pattern in TypeScript:
- Define interface Repository<T> with methods like add(item: T) and get(id: number).
- Implement class RepositoryImpl<T> with a Map to store items.
- Create instances for specific types, e.g., RepositoryImpl<{name: string}>.
- Use add to store and get to retrieve items by id.
- Enables reusable data management for any type T.
Full Transcript
This visual execution traces the generic repository pattern in TypeScript. First, we define a generic interface Repository<T> with add and get methods. Then, we implement RepositoryImpl<T> using a Map to store items keyed by number. We create an instance userRepo for objects with a name property. When we add an item, the Map stores it with key 1. When we get by id 1, the stored object is returned. Variables userRepo.items and user track the Map and retrieved object states. Key moments clarify why the Map size changes and how types are used. The quiz tests understanding of Map size after add, when user is assigned, and get behavior for missing keys. This pattern helps manage data generically and reuse code for different types.

Practice

(1/5)
1.

What is the main benefit of using the Generic Repository Pattern in TypeScript?

easy
A. It allows handling multiple data types with one reusable class.
B. It automatically generates UI components.
C. It replaces the need for TypeScript interfaces.
D. It compiles TypeScript code faster.

Solution

  1. Step 1: Understand the purpose of the generic repository

    The generic repository pattern is designed to handle data operations for many types using one class or interface.
  2. Step 2: Compare options with the pattern's purpose

    Only It allows handling multiple data types with one reusable class. correctly describes this benefit. Options A, B, and C are unrelated to the pattern's main use.
  3. Final Answer:

    It allows handling multiple data types with one reusable class. -> Option A
  4. Quick Check:

    Generic repository = reusable data handler [OK]
Hint: Generic means reusable for many types [OK]
Common Mistakes:
  • Thinking it generates UI components
  • Confusing it with TypeScript compilation
  • Believing it removes interfaces
2.

Which of the following is the correct way to declare a generic repository interface in TypeScript?

interface IRepository<T> {
  add(item: T): void;
  get(id: number): T | undefined;
}
easy
A. interface IRepository<T> { add(item: T): void; get(id: number): T | undefined; }
B. interface IRepository { add(item: any): void; get(id: number): any; }
C. interface IRepository<T> { add(item: any): void; get(id: number): T; }
D. interface IRepository<T> { add(item: T): T; get(id: string): T | undefined; }

Solution

  1. Step 1: Check generic syntax correctness

    interface IRepository<T> { add(item: T): void; get(id: number): T | undefined; } correctly declares a generic interface with type parameter <T> and uses it consistently in method signatures.
  2. Step 2: Validate method signatures

    interface IRepository<T> { add(item: T): void; get(id: number): T | undefined; }'s methods use item: T and return T | undefined for get, which is correct. Other options misuse types or parameter types.
  3. Final Answer:

    interface IRepository<T> { add(item: T): void; get(id: number): T | undefined; } -> Option A
  4. Quick Check:

    Generic interface syntax = interface IRepository<T> { add(item: T): void; get(id: number): T | undefined; } [OK]
Hint: Generic interfaces use <T> and consistent types [OK]
Common Mistakes:
  • Using 'any' instead of generic type T
  • Mismatching parameter and return types
  • Using wrong id parameter type
3.

Given the following generic repository class, what will be the output of the console.log statement?

class Repository<T> {
  private items: T[] = [];
  add(item: T): void {
    this.items.push(item);
  }
  getAll(): T[] {
    return this.items;
  }
}

const repo = new Repository<string>();
repo.add('apple');
repo.add('banana');
console.log(repo.getAll());
medium
A. Error: Type mismatch
B. ['apple']
C. []
D. ['apple', 'banana']

Solution

  1. Step 1: Understand the generic repository usage

    The repository is created for strings, so it stores string items in an array.
  2. Step 2: Trace the add and getAll methods

    Two strings 'apple' and 'banana' are added, so getAll returns ['apple', 'banana'].
  3. Final Answer:

    ['apple', 'banana'] -> Option D
  4. Quick Check:

    Added items = ['apple', 'banana'] [OK]
Hint: Add items then getAll returns all added [OK]
Common Mistakes:
  • Expecting only one item in output
  • Thinking getAll returns empty array
  • Confusing type errors
4.

Identify the error in this generic repository implementation:

class Repository<T> {
  private items: T[] = [];
  add(item: T): void {
    this.items.push(item);
  }
  get(id: number): T {
    return this.items[id];
  }
}

const repo = new Repository<number>();
console.log(repo.get(0));
medium
A. The items array should be public, not private.
B. The add method should return the added item.
C. The get method should return T | undefined to handle missing items.
D. The generic type T cannot be used with arrays.

Solution

  1. Step 1: Analyze the get method return type

    The get method returns this.items[id], which may be undefined if the index is invalid.
  2. Step 2: Check return type compatibility

    Since undefined is possible, the return type should be T | undefined to avoid runtime errors.
  3. Final Answer:

    The get method should return T | undefined to handle missing items. -> Option C
  4. Quick Check:

    Return type must allow undefined for missing index [OK]
Hint: Allow undefined return for invalid index [OK]
Common Mistakes:
  • Ignoring possible undefined return
  • Changing add method unnecessarily
  • Making items public without reason
5.

You want to create a generic repository that supports updating an item by id. Which method signature correctly fits this requirement?

hard
A. update(item: T): void
B. update(id: number, item: T): boolean
C. update(id: string, item: any): T
D. update(id: number): T | undefined

Solution

  1. Step 1: Understand update method needs

    To update an item, you need to specify which item by id and provide the new item data.
  2. Step 2: Evaluate method signatures

    update(id: number, item: T): boolean takes an id and a new item of type T, returning boolean to indicate success, which fits the requirement best.
  3. Final Answer:

    update(id: number, item: T): boolean -> Option B
  4. Quick Check:

    Update needs id and new item, returns success [OK]
Hint: Update needs id and new item, returns success boolean [OK]
Common Mistakes:
  • Missing id parameter
  • Using wrong parameter types
  • Returning wrong type from update
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard