# Convert your Typescript codebase to No Unchecked Indexed Access

Learn how to enable noUncheckedIndexedAccess in your tsconfig. Discover heuristics and automation tips to migrate large TypeScript codebases smoothly.

Written by [Greg Dingle](#)

Published Nov 2026 - 5 min read

[![A typescript code displayed on a screen](https://flux.seobdtools.com/p/assets/blogs/nouncheckedindexed/main.webp)](#)

### TypeScript noUncheckedIndexedAccess

Flux.ai was started around 3 years ago in TypeScript with the [default compiler settings](https://www.typescriptlang.org/tsconfig). If we could go back in time, there is one setting we would surely change: [noUncheckedIndexedAccess](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess). By default, this setting is false. [Many people believe](https://github.com/microsoft/TypeScript/issues/49169) it should be true.

### What is tsconfig noUncheckedIndexedAccess?

What does noUncheckedIndexedAccess do? By default, TypeScript assumes any array element or object property you access dynamically actually exists:

```typescript
function uppercaseFirst(str: string) {
  return str[0].toUpperCase() + str.slice(1);
}

uppercaseFirst('') // runtime error BAD!!!
```

In the example above, the function will throw an error if the string is empty, because str\[0\] returns undefined and doesn't have a toUpperCase function. TypeScript doesn't warn you about that, regardless of whether strict mode is enabled or not. This is a huge hole in type safety.

The flag noUncheckedIndexedAccess will plug that hole and force you to deal with the possible undefined:

```typescript
function uppercaseFirst(str: string) {
  return str[0]?.toUpperCase() + str.slice(1); // note: ? nullish operator
}

uppercaseFirst('') // returns ''
```

So, why can't we just turn on noUncheckedIndexedAccess? You can, but in a large codebase like that of Flux.ai, you are likely to get thousands of type errors. We had 2761 errors across 373 files! For one speedy engineer converting one file every minute, it would have taken 6+ hours of mind-numbing work to convert all 373 files.

The solution we describe here is how to smoothly convert your codebase with some simple heuristics and automation.

### Heuristics for noUncheckedIndexedAccess

According to Wikipedia, [a heuristic technique](https://en.wikipedia.org/wiki/Heuristic)

> is any approach to problem solving or self-discovery that employs a practical method that is not guaranteed to be optimal, perfect, or rational, but is nevertheless sufficient for reaching an immediate, short-term goal or approximation.

That is definitely true here.

The goal was to get the codebase compiling with the new flag, not to fix any bugs. The fixing can come later.

To that end, we intentionally added [type assertions](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator) ! to suppress all new type errors from undefined types without changing the runtime behavior of the code.

```typescript
const firstLetter = str[0] // needs too be str[0]!
const rest = str.slice(1)
const upperFirst = firstLetter.toUpperCase()
```

Expanding the scope of replacements to preceding lines allowed us then to automate more fixes with few false positives.

### Automating the TypeScript Migration

The full script we ran on our codebase is below. Note: it did not fix all the errors. It fixed around 2400 out of 2761 errors, leaving around 100 files for us to fix by hand.

Pro-tip: when experimenting with the replacers and precede, you can simply reset your changes with git reset --hard HEAD (assuming you are working in a git repo).

```typescript
#!/usr/bin/env ts-node

// To generate noUncheckedIndexedAccess.txt, run
// $ npx tsc | grep 'error T' > noUncheckedIndexedAccess.txt

import {readFileSync, writeFileSync} from "fs";

type ErrorLines = {path: string; lineNum: number; message: string}[];

// NOTE: these should be idempotent for safety!
const replacers: [RegExp, string][] = [
    [/(\w+\.\w+\.\w+)\.(\w+)/g, "$1!.$2"], // a.b.c.d to a.b.c!.d
    [/(\w+\[(\w|\.)+\])!*/g, "$1!"], // add ! after []
    [/(\w+\])(\[\w+\])/g, "$1!$2"], // add ! between [][]
    [/(\[\w+\])(\.\w+)/g, "$1!$2"], // add ! between [] and .
    [/(\[\d\]?)!*/g, "$1!"], // add ! after [0]
    // START CORRECTIONS
    [/\]!\) =>/g, "]) =>"], // correcting add ! above
    [/\]! =/g, "] ="], // correcting add ! above
];

const precede = 2;

function main() {
    const txt = readFileSync("./noUncheckedIndexedAccess.txt", "utf-8");
    const errorLines = parseErrorLines(txt);
    errorLines.forEach((errorLine) => {
        let lineText = readLine("../" + errorLine.path, errorLine.lineNum, precede) as string;
        replacers.forEach(([match, replacement]) => {
            const newLineText = getNewLineText(lineText, match, replacement);
            if (newLineText) lineText = newLineText;
        });
        console.log("\n---");
        console.log(errorLine.path, errorLine.lineNum, "\n", lineText);
        console.log("---\n");
        writeLine("../" + errorLine.path, errorLine.lineNum, lineText, precede);
    });
}

function getNewLineText(lineText: string, match: RegExp, replacement: string) {
    return (
        lineText
            .split("\n")
            // @ts-ignore: ignore missing string method
            .map((line) => line.replaceAll(match, replacement))
            .join("\n")
    );
}

function parseErrorLines(txt: string): ErrorLines {
    return txt
        .split("\n")
        .filter(Boolean)
        .map((line) => {
            const [pathPlus, message] = line.split(": error ");
            const pieces = pathPlus?.split("(");
            if (!pieces || !pieces[0] || !pieces[1] || !message) {
                throw new Error(`Missing bits in line: ${line}`);
            }
            const numberPieces = pieces[1].split(",", 1);
            if (!numberPieces || !numberPieces[0]) {
                throw new Error(`Missing numbers in pieces: ${pieces}`);
            }
            const lineNum = parseInt(numberPieces[0], 10);
            if (!(lineNum > 0 && lineNum < 1000000)) {
                throw new Error(`Bad line number: ${lineNum}`);
            }
            return {
                path: pieces[0],
                lineNum,
                message,
            };
        });
}

function readLine(filename: string, lineNum: number, precede: number) {
    const lines = readFileSync(filename, "utf8").split("\n");
    return lines.slice(lineNum - 1 - precede, lineNum).join("\n");
}

function writeLine(filename: string, lineNum: number, lineText: string, precede: number) {
    const lines = readFileSync(filename, "utf8").split("\n");
    lines.splice(lineNum - 1 - precede, precede + 1, ...lineText.split("\n"));
    writeFileSync(filename, lines.join("\n"));
}

main();
```

## About the author

Greg Dingle

Software Engineer at Flux

Building the future with friends

Let’s start building

Build your big idea together with Flux. All you need to do is describe what you want to make, or what problem you want to solve. Flux will work with you to make it a reality.

![Close-up photo of a PCB made with Flux, with an ESP32 module, buttons, and a microSD slot.](https://flux.seobdtools.com/p/assets/blogs/miscellaneous/sidebar-cta-pcb.webp) [Try it Free](https://flux.seobdtools.com/signup)

## Related Content

[

![Teal isometric illustration of an SDR circuit board in an open enclosure, with a regulator feedback divider highlighted for inspection](https://flux.seobdtools.com/p/assets/blogs/first-spin-debug-imported-altium-board/main.webp)

### Debug an Imported Altium Board From the First-Spin Symptom

3 Min Read](https://flux.seobdtools.com/p/blog/first-spin-debug-imported-altium-board)[

![Teal technical illustration of two circuit boards in open enclosures, with an exponential step-response curve and a low-pass response diagram](https://flux.seobdtools.com/p/assets/blogs/spice-simulation-imported-altium-cadence-boards/main.webp)

### Run SPICE on Your Imported Altium and Cadence Boards

4 Min Read](https://flux.seobdtools.com/p/blog/spice-simulation-imported-altium-cadence-boards)[

![a space-themed background with stars and a floating astronaut helmet to the left. Large, bold text reads '6 Prompting Tips to Get the Most Out of Copilot', indicating a focus on maximizing the use of a digital assistant or tool.](https://flux.seobdtools.com/p/assets/blogs/copilot-6-prompting-tips/main.webp)

### 6 Prompting Tips to Get the Most Out of Copilot

6 Min Read](https://flux.seobdtools.com/p/blog/6-prompting-tips-to-get-the-most-out-of-copilot)[

![Isometric teal line drawing on a dark background: a circuit board and its open enclosure on a workbench, with connected checkpoints that lead from a plan to a finished device](https://flux.seobdtools.com/p/assets/blogs/ai-adoption-hardware-teams-customer-success/main.webp)

### AI Adoption Is a People Problem: How Flux Customer Success Gets Hardware Teams to ROI

7 Min Read](https://flux.seobdtools.com/p/blog/ai-adoption-hardware-teams-customer-success)[

![Glowing teal isometric line art of an isolated AC/DC power board with a transformer across a slot, next to a metal enclosure, on a dark background](https://flux.seobdtools.com/p/assets/blogs/creepage-and-clearance-pcb/main.webp)

### Creepage vs. Clearance: A Worked Example on an Isolated Power Board

10 Min Read](https://flux.seobdtools.com/p/blog/creepage-and-clearance-pcb)[

![High speed pcb design traces glowing over a complex pcb board. Visible are serpentine length-matched traces.](https://flux.seobdtools.com/p/assets/blogs/high-speed-design/main.webp)

### Crosstalk, Reflections, and the Real Challenges of Signal Integrity

2 Min Read](https://flux.seobdtools.com/p/blog/crosstalk-reflections-and-the-real-challenges-of-signal-integrity)

See All (6)

## Let’s start building.

Let’s build your idea together. All you need to do is describe what you want to make, or what problem you want to solve. Flux will work with you to make it a reality.

[Try it Free](https://flux.seobdtools.com/signup)

 ![Close-up of a Flux-designed printed circuit board](https://flux.seobdtools.com/p/assets/homepage-v2/mission-visual.webp)
