Generics

How to build powerful abstractions with generic programming

function foo<A, B>(xs: A[], func: (x: A) => B): B[] {   /* ... */ }

Motivation

const numbers = [1, 2, 3, 4, 5, 6]
const isEven = (num: number): boolean => num % 2 === 0
const res = numbers.filter(isEven)console.log(res)
// --> [2, 4, 6]
function filter(xs: number[], func: (x: number) => boolean): number[] {
cons res: number = []
for (const x of xs) {
if (func(x)) {
res.push(x)
}
}
return res
}
const res = filter(numbers, isEven)console.log(res)
// --> [2, 4, 6]
const names = ['Alice', 'Bob', 'John', 'Alex', 'Pete', 'Anthony']
const startsWithA = (name: string): boolean => name.toLowerCase().charAt(0) === 'a'
const res = filter(names, startsWithA)console.log(res)
// --> Type Error
function filter(xs: number[], func: (x: number) => boolean): number[] {   /* ... */ }

Entering Generics

function filter<T>(xs: T[], func: (x: T) => boolean): T[] {
const res: T[] = []
for (const x of xs) {
if (func(x)) {
res.push(x)
}
}
return res
}
let res// using `filter` with numbers and our `isEven` function
res = filter(numbers, isEven)
console.log(res)
// --> [2, 4, 6]
// using `filter` with strings and our `startsWithA` function
res = filter(names, startsWithA)
console.log(res)
// --> ['Alice', 'Alex', 'Anthony']

Function signatures as documentation

function foo<A, B>(xs: A[], func: (x: A) => B): B[] {   /* ... */ }
const number = [1, 2, 3, 4, 5, 6]
const numToString = (num: number): string => num.toString()
const res = map(numbers, numToString)console.log(res)
// --> ['1', '2', '3', '4', '5', '6']

Conclusion

👨‍💻 Maker — 👨‍🏫 Lifelong learner — Co-creator of the Serverless Framework — https://philippmuens.com