Skip to content

Conversation

@som-sm
Copy link
Collaborator

@som-sm som-sm commented Oct 19, 2025

This PR proposes a significantly simpler implementation for #1105. Closes #369.

Opened a new PR as I didn’t want to overwrite the existing one, since it introduces several helper types that might be useful in general. We could potentially repurpose that PR to expose some of those utility types.

@sindresorhus
Copy link
Owner

Can you summarize how it's simpler? Any downsides?

@som-sm
Copy link
Collaborator Author

som-sm commented Oct 19, 2025

Can you summarize how it's simpler? Any downsides?

Yeah yeah, I'll add all the details once I make the PR "Ready for review".

@andr3medeiros
Copy link

I'm a bit confused by the documentation: It shows some results as union, which it would not be the real translation of JS Array.at right?

Is the below example something similar to what you want to achieve?

import { ArraySlice, GreaterThan, IsNegative, LastArrayElement } from 'type-fest';

export type IsPositive<N extends number> = IsNegative<N> extends true ? false : true;
type MakePositive<N extends number> =
	IsPositive<N> extends true ? N : `${N}` extends `${infer _}${infer R extends number}` ? R : N;
	
type ArrayAt<TArray extends readonly unknown[], TIndex extends number> =
	IsPositive<TIndex> extends true
		? TArray[TIndex]
		: GreaterThan<number & MakePositive<TIndex>, TArray['length']> extends true
			? undefined
			: ArraySlice<TArray, TIndex>[0];

type Arr = [1, 2 | undefined, 3];

type Test = ArrayAt<Arr, 1>; // 2 | undefined
type Test2 = ArrayAt<Arr, -4>; // undefined
type Test3 = ArrayAt<Arr, -3>; // 1
type Test4 = ArrayAt<Arr, -2>; // 2 | undefined
type Test5 = ArrayAt<Arr, -1>; // 3
type Test6 = ArrayAt<Arr, 4>; // undefined

Repository owner deleted a comment from claude bot Oct 23, 2025
Repository owner deleted a comment from claude bot Oct 23, 2025
@som-sm
Copy link
Collaborator Author

som-sm commented Oct 23, 2025

I'm a bit confused by the documentation: It shows some results as union, which it would not be the real translation of JS Array.at right?

@andr3medeiros For array types that contain optional elements or rest element, the answer could be a union because the type could hold different values at runtime. For example, the array type ['a', 'b', 'c', ...number[], 'd'] could hold values like the following at runtime:

['a', 'b', 'c', 'd']
['a', 'b', 'c', 1, 'd']
['a', 'b', 'c', 1, 2, 'd']
['a', 'b', 'c', 1, 2, 3, 'd']

Now, if you index -3 from ['a', 'b', 'c', ...number[], 'd'], the result has to be a union of all possible outcomes, like:

['a', 'b', 'c', 'd'].at(-3) // 'b'
['a', 'b', 'c', 1, 'd'].at(-1) // 'c'
['a', 'b', 'c', 1, 2, 'd'].at(-1) // 1
['a', 'b', 'c', 1, 2, 3, 'd'].at(-1) // 2

So, therefore the final result of ArrayAt<['a', 'b', 'c', ...number[], 'd'], -3> is 'b' | 'c' | number. The type makes sure it covers all possible outcomes.

@andr3medeiros
Copy link

@som-sm you're right about the array spread problem. But it got me thinking on this outcome thing. We're talking about touples, not arrays right? Array items are unpredictable. Given that I believe ArrayAt<['a', 'b', 'c', ...number[], 'd'], -1> should indeed be "d", ArrayAt<[1, 2 | undefined], 1> should be 2 | undefined.

Repository owner deleted a comment from claude bot Oct 24, 2025
@som-sm
Copy link
Collaborator Author

som-sm commented Oct 24, 2025

We're talking about tuples, not arrays right?

@andr3medeiros Both arrays and tuples. Moreover, you can argue that something like ['a', 'b', ...number[]] is also a tuple. And, surely something like ['a', 'b', 'c'?, 'd'?] is a tuple, so ArrayAt<['a', 'b', 'c'?, 'd'?], -2> has to be a union of all possible outcomes, i.e., 'a' | 'b' | 'c' (or 'a' | 'b' | 'c' | undefined).


Given that I believe ArrayAt<['a', 'b', 'c', ...number[], 'd'], -1> should indeed be "d", ArrayAt<[1, 2 | undefined], 1> should be 2 | undefined.

Yeah, correct!

This PR is still a WIP, but you can go through the test cases to better understand the different scenarios.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add ArrayAt type for stricter Array.prototype.at() with tuples

4 participants