Skip to content

Why Get-Or-Throw?

The Core Question

When you access an array element or object property by a dynamic key, the value might not be there. TypeScript's noUncheckedIndexedAccess makes this explicit by adding | undefined to every indexed access. But how should you handle that undefined?

There are two fundamental approaches:

  1. Defensive programming — check for undefined and handle the absence gracefully
  2. Fail-fast — assert the value exists and throw immediately if it doesn't

Get-Or-Throw is for the second case: when absence is a bug, not a valid state.

When to Use got()

Use got() when a missing value means something has gone wrong — a business invariant has been violated, data is corrupt, or an earlier operation failed silently:

ts
// The config must have a "database" section
const dbConfig = got(config, "database");

// There must be at least one item after filtering
const first = got(filtered, 0, "Filter returned no results");

// Parallel arrays must have the same length
const label = got(labels, index);

In these cases, continuing with undefined would cause confusing downstream errors. Failing immediately with a clear message is better.

When NOT to Use got()

When absence is a legitimate possibility, use native patterns instead:

ts
// User might not exist — that's fine
const user = users.find((u) => u.id === id);
if (!user) {
  return notFound();
}

// Optional configuration
const timeout = config.timeout ?? 5000;

// Array might be empty
const first = items[0];
if (first === undefined) {
  showEmptyState();
}

If the code after the access handles the undefined case meaningfully, you don't need got().

When to Use Tuple Types Instead

If the length of an array is statically known, TypeScript can track individual element types without | undefined. In those cases, prefer a tuple type over got():

ts
// TypeScript knows this has exactly 3 elements
const rgb: [number, number, number] = [255, 128, 0];
const red = rgb[0]; // number, not number | undefined

Problems with Defensive Patterns

Silent Failures

The most dangerous pattern is silently swallowing undefined:

ts
// Bug: if arr[i] is undefined, localeCompare is called on undefined
arr.sort((a, b) => {
  const valA = mapping[a]; // possibly undefined
  const valB = mapping[b]; // possibly undefined
  return valA.localeCompare(valB); // runtime crash, no clear message
});

With got(), the failure is immediate and descriptive:

ts
arr.sort((a, b) => {
  const valA = got(mapping, a);
  const valB = got(mapping, b);
  return valA.localeCompare(valB); // safe — both are guaranteed strings
});

Verbose Guard Clauses

Without got(), you end up with repetitive null checks:

ts
const value = obj[key];
if (value === undefined) {
  throw new Error(`Expected key "${key}" to exist`);
}
// now use value

This pattern repeated across a codebase adds significant noise. got() reduces it to a single expression:

ts
const value = got(obj, key);

Inconsistency

Different developers write different error messages, check in different ways (=== undefined, != null, in operator), or sometimes forget to check at all. got() provides a single consistent pattern.

Addressing Objections

"It's a dependency for something trivial"

Get-Or-Throw has zero dependencies and is only a few lines of code. The value isn't in the implementation complexity — it's in the consistency and expressiveness it brings to a codebase. A shared convention is worth more than any individual helper function.

"Throwing crashes the app"

That's the point. If a business invariant is violated, you want to know immediately. The alternative — continuing with undefined — leads to corrupted data, confusing errors far from the source, and bugs that are hard to reproduce.

"I can just use the non-null assertion (!)"

The non-null assertion (value!) tells TypeScript to trust you, but provides no runtime safety. If you're wrong, you get a confusing undefined error somewhere downstream. got() gives you both the type narrowing and a clear runtime error at the point of access.

Released under the MIT License.