#at(-1) type guard
89 messages · Page 1 of 1 (latest)
at is just a function, it has no relation to the length check - and also sparse arrays technically exist
would x satisfy MyType[] though?
yes
all the "elements" (of which there are 0) fulfill MyType
it's kind of like assigning an empty array
can I guard against sparse arrays instead?
i don't think you reasonably can, and i don't think it'd be particularly useful. typescript doesn't have a way to represent "dense array" in types anyways
so what is the correct way of specifying my use case
you can't change the return type of at through any narrowing
i'd just get the element and narrow the element itself, rather than the array
where does file come from in the first place?
yeah probably this
somewhere. it is guaranteed to be MyType[]
mkantor's probably asking to avoid this => https://xyproblem.info
oh, ignore the typo 🙂
if you can guarantee that it's [MyType] instead (or somesuch) from the beginning that'd also work, which is why i asked where it comes from
I can, but TypeScript is not convinced
i think your question is basically https://github.com/microsoft/TypeScript/issues/28837 (not sure why that issue talks about spreading so much; this is really whether it's a tuple with a known length or not)
show us the code
.at would not change regardless of narrowing
but [] would
i'm not seeing a [string] there or whatever
right, I can [] even if I think it's ugly
I dun goofed this is the correct jetbrains output
right so readonly GalleryFile[] and [GalleryFile] are not the same thing
and as mentioned before, .at just won't have a different type no matter how you narrow
do you know what tuples are?
I can do file.path[file.path.length - 1] like a barbarian ES5 speaker
it also wouldn't be narrowed
it does
ah, you have nUIA disabled
it's not narrowed
it just doesn't have | undefined in to begin with
what's the full name of this tsconfig key?
to be fair I inherited this project and the tsconfigs are out of whack
you could do file.path.length + 40 and it'd still say GalleryFile
noUncheckedIndexedAccess
but yeah if it can't be a tuple, just use at(-1) and check the result
in case it wasn't clear, what chris meant [up here](#1484515881521250386 message) (and also immediately above) is this:
const currentFolder = files.at(-1);
if (currentFolder === undefined) {
throw new Error('No folders found');
}
// the rest of your code goes here (where `currentFolder` will be narrowed)
yes, that is exactly the boilerplate I want to avoid
but if I have no other option then shrug emoji
ah, that changes things
you either have to prove to typescript that files is not empty before this code, or you have to deal with the possibility that it may be empty. you haven't really shown enough context for us to suggest ways to do the former
there's also the option of using !, but.. why use an assertion when you can narrow directly lol
(i guess a third forth option is writing a custom type guard function to narrow files from readonly GalleryFile[] to readonly [GalleryFile], but i'm not sure if that'll be sufficient since you specifically want the last element)
files has already been checked by a Supreme Entity (valibot) to strictly conform to MyType[] with no holes (unless there are other tsconfig flags I can turn on to show the contrary)
actually wait, can there be more than one element? if so i might've been causing confusion with my [GalleryFile] mentions
those aren't communicated to typescript though
you can probably use valibot to also validate that the array is non-empty, if that helps
(i'm not a valibot user)
even if it were [GalleryFile, ...GalleryFile[]] that still wouldn't change the types of x.at(number) or x[number]
so this might be the issue?
not sure what you mean by that
as it stands you just aren't really narrowing anything
yeah, but if they can do x[0] it would
const reversedX = x.reverse()
if (validateItsNotEmpty(reversedX)) {
const whatIWant = reversedX[0]
}
(kidding; don't do this when there are multiple better options)
i was thinking about that too lmao
O(n) array access to satisfy static requirements..
either way, typescript says it is a GalleryFile[] so I don't have a reason to believe otherwise when at() ing. Is there no way to tell typescript the minimum length of an array on a type level?
(this is entering curiosity level)
there's a tuple, but typescript is a static type system so it'd have to be a specific length you check for and you'd also need to cast/predicate since typescript doesn't narrow from an array to a tuple
Yeah it's not so much a tuple and more that the dynamic array can have at least 5 or 7 elements etc
I'm not surprised that it can't though
at including undefined isn't about the array including undefined, it's about at retrieving elements out of bounds
you can model this with tuples that have rest elements (like [A, A, A, ...A[]] is "an array of As with at least three elements")
yes that I get. I thought some built-in functions in JS get type guards for free if I check for length, etc (or maybe I'm dreaming)
yeah no they can't
i've used custom type guards like this before to do this kind of narrowing (but from what i understand of your scenario, probably would not bother in your case):
well that's a bubble of mine burst
/** Creates a readonly tuple containing `T` repeated `N` times. */
type Repeat<T, N extends number> = number extends N ? readonly T[] : BuildTuple<T, N, readonly []>
type BuildTuple<T, N extends number, A extends readonly T[]> = A extends { length: N }
? A
: BuildTuple<T, N, readonly [...A, T]>
const isAtLeastOfLength =
<N extends number>(length: N) =>
<A>(as: readonly A[]): as is Repeat<A, N> & readonly A[] =>
as.length >= length
const stuff = Array.from({ length: Math.random() * 10 }, _ => 'blah')
const mightNotBeDefined = stuff[2]
// ^? - const mightNotBeDefined: string
if (isAtLeastOfLength(3)(stuff)) {
const definitelyDefined = stuff[2]
// ^? - const definitelyDefined: string
}
this is the feature that the issue i linked to before is requesting (it doesn't currently exist)
needs nUIA i think?
!solved
that's nothing. there are TS issues that have been open since 2015
I have an issue for nextjs that is old enough to attend elementary school
I contemplated sending a "good luck on your first day at school" card to vercel
/** Creates a readonly tuple containing `T` repeated `N` times. */
type Repeat<T, N extends number> = number extends N ? readonly T[] : BuildTuple<T, N, readonly []>
type BuildTuple<T, N extends number, A extends readonly T[]> = A extends { length: N }
? A
: BuildTuple<T, N, readonly [...A, T]>
const isAtLeastOfLength =
<N extends number>(length: N) =>
<A>(as: readonly A[]): as is Repeat<A, N> & readonly A[] =>
as.length >= length
const stuff = Array.from({ length: Math.random() * 10 }, _ => 'blah')
const mightNotBeDefined = stuff[2]
// ^? - const mightNotBeDefined: string | undefined
if (isAtLeastOfLength(3)(stuff)) {
const definitelyDefined = stuff[2]
// ^? - const definitelyDefined: string
}
(the above snippet with nUIA enabled)