Array Reference Semantics

Arrays are reference values: mutating an array parameter in place grows the caller's array, exactly like JavaScript. Reassigning the parameter to a new array, by contrast, is a local rebind and leaves the caller's array untouched.

function enqueue(queue: string[], item: string): void {
  queue.push(item)
}

function drainFirst(queue: string[]): string {
  const head = queue.shift()
  return head!
}

const jobs: string[] = ["build"]
enqueue(jobs, "test")
enqueue(jobs, "deploy")
console.log("after enqueue:", jobs.join(" -> "))   // build -> test -> deploy
console.log("length:", jobs.length)                // 3

const first = drainFirst(jobs)
console.log("drained:", first)                     // build
console.log("remaining:", jobs.join(" -> "))       // test -> deploy

// Reassigning the parameter is local — the caller's array is unchanged.
function withoutDeploys(queue: string[]): number {
  queue = queue.filter((j) => j !== "deploy")
  return queue.length
}
console.log("filtered length:", withoutDeploys(jobs))  // 1
console.log("caller intact:", jobs.join(" -> "))       // test -> deploy

// Propagation is transitive through nested calls.
function pipeline(stages: number[]): void {
  addStage(stages, 4)
}
function addStage(stages: number[], n: number): void {
  stages.push(n)
}
const stages: number[] = [1, 2, 3]
pipeline(stages)
console.log("stages:", stages.join(","))           // 1,2,3,4

// The same reference semantics hold for every argument shape, not just a
// named variable: an object field, a nested element, and a HOF callback
// element all pass the SAME array, so in-place mutation propagates.
const registry = { tags: ["a"] }
enqueue(registry.tags, "b")                        // through an object field
console.log("field:", registry.tags.join(","))     // a,b

const matrix: number[][] = [[1], [10, 20]]
addStage(matrix[0], 2)                             // through a nested element
console.log("element:", matrix[0].join(","))       // 1,2

matrix.forEach((row) => row.push(0))               // through a HOF element
console.log("rows:", matrix[0].join(","), matrix[1].join(",")) // 1,2,0 10,20,0

const row = matrix.find((r) => r.length === 3)     // a found element aliases
if (row) { row.push(99) }
console.log("found:", matrix[0].join(","))         // 1,2,0,99

// A transient copy stays detached: slice() hands the callee a new array.
enqueue2(matrix[1].slice())
console.log("detached:", matrix[1].length)         // 3
function enqueue2(xs: number[]): void { xs.push(-1) }
View source on GitHubexamples/arrays/array_reference_semantics.ts