GO
TS

Go to TypeScript

10 lessons

Progress0%
1Variables & Types2Functions3Structs & Classes4Interfaces — Both Use Structural Typing5Error Handling6Goroutines vs Async/Await7Slices & Maps → Arrays & Objects8Packages & Modules9Testing10Generics
All Mirror Courses
GO
TS
Generics
MirrorLesson 10 of 10
Lesson 10

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.

Mirror Card
GO
From Go:

In Go, you're familiar with go generics (1.18+) vs typescript generics — constraints and type parameters.

TS
In TypeScript:

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:

TS
TypeScript 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()); // 2

Comparing to Go

Here's how you might have written similar code in Go:

GO
Go (What you know)
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()) // 2
Mirror Card
GO
From Go:

You may be used to different syntax or behavior.

TS
In TypeScript:

Go uses [T constraint] after function/type name; TypeScript uses <T extends constraint>

Mirror Card
GO
From Go:

You may be used to different syntax or behavior.

TS
In TypeScript:

Go's cmp.Ordered covers all comparable types; TypeScript needs explicit union in constraint

Mirror Card
GO
From Go:

You may be used to different syntax or behavior.

TS
In TypeScript:

Go type inference often works without specifying T; TypeScript also infers in most cases

Mirror Card
GO
From Go:

You may be used to different syntax or behavior.

TS
In TypeScript:

Go has no variance (covariance/contravariance); TypeScript interfaces support out/in

Mirror Card
GO
From Go:

You may be used to different syntax or behavior.

TS
In TypeScript:

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.

GO
Go
function identity<T>(v: T): T { return v; }
TS
TypeScript
func Identity[T any](v T) T { return v }
// Usage: Identity(42) — T inferred as int

2. Constraints

Go uses interface constraints; cmp.Ordered is a built-in for numeric/string comparisons. TypeScript uses 'extends'.

GO
Go
function max<T extends number | string>(a: T, b: T): T { return a > b ? a : b; }
TS
TypeScript
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.

GO
Go
class Box<T> { constructor(public value: T) {} }
TS
TypeScript
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.

GO
Go
function zip<A, B>(as: A[], bs: B[]): [A, B][] {...}
TS
TypeScript
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
Common Pitfall
Don't assume TypeScript works exactly like Go. While the concepts may be similar, the syntax and behavior can differ significantly.

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)
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your Go code in TypeScript to practice these concepts.
PreviousFinish