Skip to content

Arrays, Slices & Maps

In JavaScript/TypeScript, arrays are dynamic by default and objects serve as maps. Go separates these concerns: fixed-size arrays, dynamic slices (the one you’ll use daily), and maps.

Go arrays have a fixed size baked into the type. You’ll rarely use them directly. Slices are the dynamic, flexible version — backed by an array but with a length and capacity that grow as needed.

TypeScript
// TypeScript — always dynamic
const nums: number[] = [1, 2, 3];
nums.push(4);
console.log(nums.length); // 4
Go
// Go — slice (dynamic)
nums := []int{1, 2, 3}
nums = append(nums, 4)
fmt.Println(len(nums)) // 4
TypeScript
const letters = ["a", "b", "c", "d"];
// slice (non-mutating)
letters.slice(1, 3) // ["b", "c"]
// length
letters.length // 4
Go
letters := []string{"a", "b", "c", "d"}
// slice expression
letters[1:3] // ["b", "c"]
// length
len(letters) // 4

Go maps are like TypeScript Record<K,V> or Map<K,V>. Keys can be any comparable type.

TypeScript
// TypeScript
const scores: Record<string, number> = {
alice: 95,
bob: 87,
};
scores["carol"] = 91;
delete scores["bob"];
console.log(scores["alice"]);
Go
// Go
scores := map[string]int{
"alice": 95,
"bob": 87,
}
scores["carol"] = 91
delete(scores, "bob")
fmt.Println(scores["alice"])
package main
import (
"fmt"
"sort"
)
func main() {
// Slice
fruits := []string{"banana", "apple", "cherry"}
fruits = append(fruits, "date")
sort.Strings(fruits)
fmt.Println("Fruits:", fruits)
// Map
count := map[string]int{}
words := []string{"go", "is", "fun", "go", "is", "great", "go"}
for _, w := range words {
count[w]++
}
fmt.Println("Word counts:", count)
}

Iterators — Go’s answer to generators and for…of (Go 1.23+)

Section titled “Iterators — Go’s answer to generators and for…of (Go 1.23+)”

Since Go 1.23 a for range loop can iterate over a function of type iter.Seq[T] (or iter.Seq2[K, V]). That is Go’s version of a JS generator: values are produced lazily, one per yield, and the loop body is the consumer. The slices and maps packages ship iterator-based helpers (slices.Values, maps.Keys, slices.Collect, slices.Sorted).

TypeScript
// TypeScript: generator + for…of
function* evens(limit: number) {
for (let n = 0; n <= limit; n += 2) yield n;
}
for (const n of evens(6)) console.log(n); // 0 2 4 6
const keys = [...new Map([["a", 1], ["b", 2]]).keys()];
Go
// Go 1.23+: a func(yield func(T) bool) is an iterator
func evens(limit int) iter.Seq[int] {
return func(yield func(int) bool) {
for n := 0; n <= limit; n += 2 {
if !yield(n) { return } // consumer broke out of the loop
}
}
}
for n := range evens(6) { fmt.Println(n) } // 0 2 4 6
// stdlib helpers return iterators; Collect/Sorted turn them back into slices
keys := slices.Sorted(maps.Keys(map[string]int{"a": 1, "b": 2})) // [a b]

The yield callback returning false is how break reaches the producer — there is no generator object to .return(). Unlike JS generators, Go iterators are plain functions, so they inline well and cost no allocation for the common cases. You can also for i := range 10 over an int since Go 1.22.

Which Go collection type grows dynamically (like a JS array)?
How do you append to a slice in Go?
What does the two-value map lookup (val, ok := m[key]) tell you?
What is the zero value returned when accessing a missing int map key?