Generic constraints with extends in Typescript - Time & Space Complexity
We want to see how the time needed to run code changes when using generic constraints with extends in TypeScript.
Specifically, how does limiting types affect the number of steps the program takes?
Analyze the time complexity of the following code snippet.
function printName<T extends { name: string }>(items: T[]): void {
for (const item of items) {
console.log(item.name);
}
}
const people = [
{ name: 'Alice' },
{ name: 'Bob' },
{ name: 'Charlie' }
];
printName(people);
This function prints the name property of each item in an array, where each item must have a name string.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Looping through each item in the array.
- How many times: Once for every element in the input array.
As the number of items grows, the function prints each name one by one.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 print steps |
| 100 | 100 print steps |
| 1000 | 1000 print steps |
Pattern observation: The steps grow directly with the number of items. Double the items, double the steps.
Time Complexity: O(n)
This means the time to run grows in a straight line with the number of items in the array.
[X] Wrong: "Using extends makes the function slower because it adds extra checks for types at runtime."
[OK] Correct: The extends constraint only helps TypeScript check types while coding. It does not add any extra steps when the program runs.
Understanding how generic constraints affect code helps you write safe and efficient functions. This skill shows you can think about both correctness and performance.
What if we changed the constraint to T extends { id: number } and printed id instead? How would the time complexity change?
Practice
What does the extends keyword do in a TypeScript generic constraint?
Solution
Step 1: Understand the role of
Theextendsin genericsextendskeyword in generics restricts the generic type to only those types that satisfy a certain condition, usually matching an interface or type shape.Step 2: Compare with other options
Options B, C, and D describe behaviors unrelated toextendsin generics. It does not make types optional, allow any type, or convert types to strings.Final Answer:
It limits the generic type to types that match a specific shape or interface. -> Option AQuick Check:
Generic constraints = limit type shape [OK]
- Thinking extends makes a type optional
- Confusing extends with type conversion
- Assuming extends removes restrictions
Which of the following is the correct syntax to constrain a generic type T to an object with a name property of type string?
function greet<T>(obj: T): string {
return `Hello, ${obj.name}`;
}Solution
Step 1: Identify correct generic constraint syntax
The correct way to constrain a generic type is usingT extends { name: string }inside the angle brackets.Step 2: Check other options for syntax errors
function greet<T = { name: string }>(obj: T): string uses assignment (=) which is for default types, not constraints. function greet<T>(obj: T extends { name: string }): string misplacesextendsoutside the angle brackets. function greet<T>(obj: T & { name: string }): string uses intersection (&) in parameter type, which is valid but not a generic constraint.Final Answer:
function greet<T extends { name: string }>(obj: T): string -> Option BQuick Check:
Use extends inside <> for constraints [OK]
extends inside <> for constraints [OK]- Using = instead of extends for constraints
- Placing extends outside generic brackets
- Confusing intersection types with constraints
What will be the output of this TypeScript code?
interface HasId { id: number; }
function getId<T extends HasId>(obj: T): number {
return obj.id;
}
const item = { id: 42, name: 'Book' };
console.log(getId(item));Solution
Step 1: Check if
The interfaceitemsatisfies the constraintHasIdrequires anidproperty of type number. The objectitemhasid: 42, so it satisfiesT extends HasId.Step 2: Understand function behavior
The function returnsobj.id, so callinggetId(item)returns 42.Final Answer:
42 -> Option DQuick Check:
Object matches constraint, returns id [OK]
- Assuming runtime error due to missing explicit type
- Confusing property existence with type errors
- Thinking generic constraints affect runtime output
Identify the error in this TypeScript code and select the fix:
function printLength<T>(arg: T): number {
return arg.length;
}Solution
Step 1: Identify cause of error
The generic typeThas no guarantee to have alengthproperty, so accessingarg.lengthcauses a compile error.Step 2: Fix by adding a constraint
AddingT extends { length: number }ensuresarghas alengthproperty, fixing the error.Final Answer:
Add constraint: <T extends { length: number }> -> Option AQuick Check:
Constraint ensures property exists [OK]
- Ignoring missing property errors
- Changing return type instead of fixing constraint
- Using any defeats generic safety
Given this interface and function, which call is valid and why?
interface Person {
name: string;
age: number;
}
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person: Person = { name: 'Alice', age: 30 };Which call is valid?
Solution
Step 1: Understand the generic constraints
The function usesK extends keyof T, so the key must be a valid property name ofT. ForPerson, valid keys are 'name' and 'age'.Step 2: Evaluate each option
getProperty(person, 'name') uses 'name', which is a valid key. getProperty(person, 'height') uses 'height', not in Person. getProperty(person, 0) uses 0, not a key. getProperty(person, 'toString') uses 'toString', which is a method on Object prototype, not a key of Person interface.Final Answer:
getProperty(person, 'name') -> Option CQuick Check:
Key must be keyof object type [OK]
- Using keys not declared in interface
- Passing numbers instead of string keys
- Confusing prototype methods with interface keys
Ready to prove it?
Pass the test and this pattern moves unknown
