Why is noUncheckedIndexedAccess enabled by default?

I started a project using create-t3-app and I was getting all these type checker errors throughout my code, which forced me to manually reassert types all over the place. Setting "noUncheckedIndexedAccess": false in tsconfig.json solved the problem with my type checker but I'm left wondering why this was enabled by default in the first place. Manually reassert types all over the place seems to run contrary to this discussion: https://www.youtube.com/watch?v=RmGHnYUqQ4k
Theo - ping․gg
YouTube
You Might Be Using Typescript Wrong...
I'm getting increasingly tired of the "but TS is so much woooooork" comments so I figured I'd do a rant. If you have friends or coworkers who are using Typescript incorrectly, please send this video to them :) Also please use Zod more it's very good https://github.com/colinhacks/zod Join the Discord: https://t3.gg/discord Follow me on Twitter...
32 Replies
julius
julius•3y ago
The rule doesn't exist just so you can non-null-assert everything, it's to protect you from accessing elements which doesn't exist.
currenthandle
currenthandleOP•3y ago
@julius so why does it exist? Is there a way to resolve this issue while noUncheckedIndexedAccess": true ? https://stackoverflow.com/questions/74503582/defined-value-giving-object-is-possibly-undefined-how-to-avoid-manually-re/74503695#74503695
Stack Overflow
Defined Value Giving: Object is possibly 'undefined'`. How to Avoid...
In this function, I am getting a type error from VS Code. The error refers to board[0]. The error states Object is possibly 'undefined' which refers to` export default function validPosition(board...
julius
julius•3y ago
Well even if array.length > 1 doesnt mean array[1] exists,
Roren
Roren•3y ago
Unless I'm mistaken it's there because it's seen as a safe and sensible default. Otherwise you can access values outside of the array's bounds and have an undefined value that TS tells you is truthy. Or, more accurately, that the compiler tells you is whatever value you typed the array as. Is it more upkeep? Yes. But TS, itself, involves more upkeep than JS. That doesn't mean it doesn't have value and won't save you from bugs.
julius
julius•3y ago
I can have array = [1,2,3,4] and without noUncheckedIndexAccess, array[100] would give me the type number
currenthandle
currenthandleOP•3y ago
currenthandle
currenthandleOP•3y ago
The way I typed with zod, if you do board[0] there will always be a number there no?
Roren
Roren•3y ago
typeof gives you the correct type, at runtime. We're talking about the typing as presented by the compiler, before run. That's why a solution to narrow that type would be to check if (typeof arr[0] === ///)
currenthandle
currenthandleOP•3y ago
So in the situation where we believe it is prudent to use noUncheckedIndexedAccess": true, what is the preferred way to to satisfy the type checker without having to do null checks and / or manual type accessions as discribed in the stackoverflow issue I linked
Roren
Roren•3y ago
Roren
Roren•3y ago
I'm curious to hear how others prefer to handle that conveniently, myself.
julius
julius•3y ago
that depends on what you want to fallback to, some alternatives:
const arr: number[]; // <-- some array

const value = arr[100] ?? 0; // fallback to 0
// ^? number

if (arr[100]) {
const value = arr[100];
console.log(value.toFixed(2); // no errors
}

if (!arr[100]) return
// continue only if defined

arr[100]?.toFixed(2);
const arr: number[]; // <-- some array

const value = arr[100] ?? 0; // fallback to 0
// ^? number

if (arr[100]) {
const value = arr[100];
console.log(value.toFixed(2); // no errors
}

if (!arr[100]) return
// continue only if defined

arr[100]?.toFixed(2);
then there are cases where you know for example arr[0] is valid, then a non-null-assertion can be fine
currenthandle
currenthandleOP•3y ago
I guess I'm trying to type it rigorously so that I don't have to configure a fallback. Maybe that's impossible or impractical?
julius
julius•3y ago
there's no typesafe way unless you check
currenthandle
currenthandleOP•3y ago
Well I feel like what ever logic I'm using in my head to detemine that I "know" should be able to be baked into the type system...
julius
julius•3y ago
you got an example?
currenthandle
currenthandleOP•3y ago
Yeah, the one I linked is an example. It is impossible for board[0] to ever be undefined board is being created from a static json file. there is not dynamic user input or api. it's loaded locally. The structure is validated by zod and there is always something there, a number. I'm sorry perhaps I'm not understanding
julius
julius•3y ago
your zod validator doesn't guarantee board[0] is defined, it validates that the array's length is > 0 counter example right here
Roren
Roren•3y ago
Generally it's not a good idea to try and get around the compiler because of what you "know". Certainly those times exist (non-null assertions are there for a reason), but in general the reason the compiler is there is to force you to "do your homework" so you don't have to rely on your own fallible understanding of the program's types. If "I know the types" were a good enough solution, TS wouldn't be useful.
circles
circles•3y ago
I like how this discussion is turning into discrete math proofs 😄 (jk)
Roren
Roren•3y ago
It can be grating writing extra conditions to go-behind when you're pretty positive. But if the state of your app changes in the future, it may end up saving you from an insidious bug.
julius
julius•3y ago
Sure but whose to say you dont call that function before the grid is validated, or the grid’s been mutated after validation and before the function call
currenthandle
currenthandleOP•3y ago
well, the grid never mutates, and I've written it so that it is the very first thing that happens. ie it won't get called before the grid is validated. So now the issue at hand is, how can I bake these facts (or the equivelent) into the type system.
julius
julius•3y ago
a simple if (!board[0]) return false; is probably what i’d do
currenthandle
currenthandleOP•3y ago
yeah so now were back to null checks i guess... just feels hacky + adding extra lines of code
julius
julius•3y ago
Idk bout that, you’re protecting against future mistakes. You may remember this today but in a week you might refactor without remebering these 3 mental steps in your head If you think thats overhead then turn off the rule, im just trying to explain its usefulness
Roren
Roren•3y ago
Particularly when working with others who may not have the same understanding about the conventions of the base, it's an extra safeguard and future-proofing.
currenthandle
currenthandleOP•3y ago
Strangely enough after resetting "noUncheckedIndexedAccess": true in my tsconfig, there is no more TS Object possibly undefined.... err in this file/function 🤦
export default function isValidPosition(
board: Grid | Board,
position: Position
) {
const [row, col] = position
const numRows = board.length
const numCols = board[0].length

return row >= 0 && row < numRows && col >= 0 && col < numCols
}
export default function isValidPosition(
board: Grid | Board,
position: Position
) {
const [row, col] = position
const numRows = board.length
const numCols = board[0].length

return row >= 0 && row < numRows && col >= 0 && col < numCols
}
Instead I'm getting 'React' refers to a UMD global, but the current file is a module. Consider adding an import instead. in my components all the sudden and it's asking me to import React from 'react' in each component file explicitly. I've never seen this before with create-t3-app
circles
circles•3y ago
Are you sure you didn't change anything else in your tsconfig? Maybe the jsx property?
currenthandle
currenthandleOP•3y ago
"jsx": "preserve", same as in my other create-t3-app projects
{
"compilerOptions": {
"target": "es2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"noUncheckedIndexedAccess": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.cjs", "**/*.mjs"],
"exclude": ["node_modules"]
}
{
"compilerOptions": {
"target": "es2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"noUncheckedIndexedAccess": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.cjs", "**/*.mjs"],
"exclude": ["node_modules"]
}
Want results from more Discord servers?
Add your server