Generics let one class, method, function, or interface work with different types without giving up type checking. A type parameter is a placeholder declared in angle brackets; a type argument supplies a specific type when the code is used. This is useful for collections: JavaScript’s Map can hold mixed keys and values, while `Map<number, TodoItem>` tells TypeScript to check for numeric keys and TodoItem values. section 12.0 section 2.2.6
Write one collection for several types
In `src/index.ts`, declare `class DataCollection<T> { private items: T[] = []; constructor(initialItems: T[]) { this.items.push(...initialItems); } add(item: T) { this.items.push(item); } getItem(index: number): T { return this.items[index]; } }`. Then create `new DataCollection<Person>(people)` or `new DataCollection<Product>(products)`. Each object retains its own type argument: the first accepts and returns Person objects; the second accepts and returns Product objects. The compiler tracks the return type without an assertion or narrowing. section 12.3 section 12.3.1 section 12.3.2
A placeholder alone does not promise that `T` has a particular property. If a method reads `item.name`, declare `class DataCollection<T extends { name: string }>`: only compatible types can instantiate it, and the method can use `name`. The constraint limits eligible types; the chosen type argument still controls what each instance accepts and returns. section 12.3.3
Place and pass type parameters according to when a type must be chosen:
- Use multiple parameters when an operation relates types, such as `DataCollection<T, U>`. Move `U` onto `collate<U>` if callers should choose a different comparison type for each call. section 12.3.4
- A subclass can pass on, narrow, or fix a superclass’s type parameter. A generic interface can similarly leave its type open for an implementing class to pass on or fix. section 12.3.6 section 12.4.2
- A static method cannot use an instance’s chosen type argument; it can declare its own. For example, `DataCollection.reverse<City>(cities)` supplies the static method’s argument. section 12.3.8
A runtime type check still needs runtime information
`filter<V extends T>() { return this.items.filter(item => item instanceof V); }` does not compile: `V` is a type, not a value available to `instanceof`. Generic types are removed during compilation. Instead, accept a type-predicate function, such as `filter<V extends T>(predicate: (target) => target is V): V[]`, and pass a predicate that can identify the required objects when JavaScript runs. section 12.3.7
Generics also carry types through JavaScript collections: `Set<T>` stores the collection’s item type, while `Map<string, T>` pairs string keys with those items. For a collection keyed by a selectable object property, `K extends keyof T` restricts the property name and `T[K]` supplies its value type. Mapped types use a parameter such as `Mapped<T>` to apply the same property transformation to different types. These advanced forms are useful when simpler constraints cannot describe the required relationship. section 13.2 section 13.4.4 section 13.5.2
Listing 13.13 Using an index type in a collection class in the index.ts file in the src folder
import { City, Person, Product, Employee } from "./dataTypes.js";
let products = [new Product("Running Shoes", 100), new Product("Hat", 25)];
//type shapeType = { name: string };
class Collection<T, K extends keyof T> implements Iterable<T> {
private items: Map<T[K], T>;
constructor(initialItems: T[] = [], private propertyName: K) {
this.items = new Map<T[K], T>();
this.add(...initialItems);
}
add(...newItems: T[]): void {
newItems.forEach(newItem =>
this.items.set(newItem[this.propertyName], newItem));
}
get(key: T[K]): T {
return this.items.get(key);
}
get count(): number {
return this.items.size;
}
[Symbol.iterator](): Iterator<T> {
return this.items.values();
}
}
let productCollection: Collection<Product, "name">
= new Collection(products, "name");
console.log(`There are ${ productCollection.count } products`);
let itemByKey = productCollection.get("Hat");
console.log(`Item: ${ itemByKey.name}, ${ itemByKey.price}`);
The collection listing puts `T`, `K extends keyof T`, and `Map<T[K], T>` together, showing how a selected property determines the map’s key type. section 13.4.4



















