Generics
Go generics (1.18+) vs TypeScript generics — constraints and type parameters
Introduction
In this lesson, you'll learn about generics in TypeScript. Coming from Go, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.
In Go, you're familiar with go generics (1.18+) vs typescript generics — constraints and type parameters.
TypeScript has its own approach to go generics (1.18+) vs typescript generics — constraints and type parameters, which we'll explore step by step.
The TypeScript Way
Let's see how TypeScript handles this concept. Here's a typical example:
// Generic function
function map<T, U>(arr: T[], f: (x: T) => U): U[] {
return arr.map(f);
}
// Generic function with constraint
function max<T extends number | string>(a: T, b: T): T {
return a > b ? a : b;
}
// Generic class
class Stack<T> {
private items: T[] = [];
push(v: T): void { this.items.push(v); }
pop(): T { return this.items.pop()!; }
}
// Interface constraint
interface Comparable { compareTo(other: this): number; }
function sort<T extends Comparable>(arr: T[]): T[] {
return [...arr].sort((a, b) => a.compareTo(b));
}
// Usage
const nums = map([1,2,3], n => n * 2);
const strs = map(["a","b"], s => s.toUpperCase());
const m = max(3, 7); // 7
const s = new Stack<number>();
s.push(1); s.push(2);
console.log(s.pop()); // 2Comparing to Go
Here's how you might have written similar code in Go:
package main
import "cmp"
// Generic function
func Map[T, U any](slice []T, f func(T) U) []U {
result := make([]U, len(slice))
for i, v := range slice {
result[i] = f(v)
}
return result
}
// Generic function with constraint
func Max[T cmp.Ordered](a, b T) T {
if a > b { return a }
return b
}
// Generic struct
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(v T) { s.items = append(s.items, v) }
func (s *Stack[T]) Pop() T {
v := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return v
}
// Usage
nums := Map([]int{1,2,3}, func(n int) int { return n*2 })
strs := Map([]string{"a","b"}, strings.ToUpper)
max := Max(3, 7) // 7
var s Stack[int]
s.Push(1); s.Push(2)
fmt.Println(s.Pop()) // 2You may be used to different syntax or behavior.
Go uses [T constraint] after function/type name; TypeScript uses <T extends constraint>
You may be used to different syntax or behavior.
Go's cmp.Ordered covers all comparable types; TypeScript needs explicit union in constraint
You may be used to different syntax or behavior.
Go type inference often works without specifying T; TypeScript also infers in most cases
You may be used to different syntax or behavior.
Go has no variance (covariance/contravariance); TypeScript interfaces support out/in
You may be used to different syntax or behavior.
Go generics are monomorphized at compile time; TypeScript generics are type-erased
Step-by-Step Breakdown
1. Generic Functions
Go uses [T any] after the function name; TypeScript uses <T>. Both infer the type parameter from arguments.
function identity<T>(v: T): T { return v; }func Identity[T any](v T) T { return v }
// Usage: Identity(42) — T inferred as int2. Constraints
Go uses interface constraints; cmp.Ordered is a built-in for numeric/string comparisons. TypeScript uses 'extends'.
function max<T extends number | string>(a: T, b: T): T { return a > b ? a : b; }import "cmp"
func Max[T cmp.Ordered](a, b T) T {
if a > b { return a }
return b
}3. Generic Structs
Generic structs work the same in both languages. Method receivers use [T] after the struct name in Go.
class Box<T> { constructor(public value: T) {} }type Box[T any] struct { Value T }
func NewBox[T any](v T) Box[T] { return Box[T]{Value: v} }
// Methods need [T any] on receiver:
func (b Box[T]) Get() T { return b.Value }4. Multiple Type Parameters
Both languages support multiple type parameters. Go separates them with commas inside brackets.
function zip<A, B>(as: A[], bs: B[]): [A, B][] {...}func Zip[A, B any](as []A, bs []B) [][2]any {
n := len(as)
if len(bs) < n { n = len(bs) }
out := make([][2]any, n)
for i := range n { out[i] = [2]any{as[i], bs[i]} }
return out
}Common Mistakes
When coming from Go, developers often make these mistakes:
- Go uses [T constraint] after function/type name; TypeScript uses <T extends constraint>
- Go's cmp.Ordered covers all comparable types; TypeScript needs explicit union in constraint
- Go type inference often works without specifying T; TypeScript also infers in most cases
Key Takeaways
- Go: [T constraint] after name; TypeScript: <T extends constraint>
- cmp.Ordered is Go's built-in constraint for < > comparisons
- Both infer type parameters from arguments — explicit T rarely needed
- Method receivers need [T] on generic types: func (s *Stack[T]) Push(v T)