Generics¶
This lesson explains generics: placeholders for types that keep relationships between inputs and outputs without resorting to any.
Generic functions¶
A generic function introduces a type parameter in angle brackets.
function first<T>(items: T[]): T | undefined {
return items[0]
}
const firstNumber = first([1, 2, 3]) // number | undefined
const firstString = first(['a', 'b']) // string | undefined
T is chosen when the function is called, based on the argument.
Generic interfaces and types¶
Types can also be generic.
interface Box<T> {
value: T
}
const numberBox: Box<number> = { value: 42 }
const stringBox: Box<string> = { value: 'hello' }
Multiple type parameters¶
You can have more than one.
Generic constraints¶
Use extends to require that a type has certain properties.
interface HasId {
id: string
}
function byId<T extends HasId>(items: T[], id: string): T | undefined {
return items.find((item) => item.id === id)
}
T can be any type that has an id string.
Default type parameters¶
If no type is supplied, T is unknown.
Generic utility pattern¶
This captures the relationship between the input array item type and the output array item type.
Hands-on: generic data store¶
- Create
src/store.ts. - Define
interface Identifiable { id: string }. - Write a generic class-like object or module
createStore<T extends Identifiable>()that returns: add(item: T): voidget(id: string): T | undefinedremove(id: string): voidall(): readonly T[]- Create one store for
interface Userand one forinterface Product. - Add, retrieve, and remove items.
- Try to add an item without an
idand observe the constraint error.