Fix `.at`

.at should not be equated with Array#slice. Since arr.at(-1) is introduced as a syntax sugar for arr[arr.length - 1], it should be equated with general property access.

1 Like

It looks like I won't be getting what I want at this point, so I created a module. I hope this will advance the discussion.

hash tag: #fix_ecmascript_at

It is not in fact a syntax sugar for arr[arr.length - 1] - [arr.at](http://arr.at)(n) is precisely syntax sugar for arr.slice(n)[0], and it behaves accordingly.

Luckily not, that would be bad.

[1].at(-5) !== [1].slice(-5)[0]

I understand what you are saying, but what JavaScript users want is a syntax sugar for arr[n >= 0 ? n : n + arr.length], not a syntax sugar for arr.slice(n)[0].

If you don't want to change it, please enlighten us that arr.at(n) is not a syntax sugar of what we want. I don't think many people really understand what that is.

1 Like

I wrote a Japanese article about Array#at, but no one is saying it is what we want it to be.

To pick up on the responses to this article:

Oh no, it's not too late, we should take it back. TypeScript's number type can't determine NaN.
いやあ今なら間に合うから取り消すべきだよ/typescriptのnumber型はNanを判定できないと思う・・・ - Fushihara のブックマーク / はてなブックマーク

This is a shame :rage: I'm not sure I'd recommend it.
【B!】【追記あり】ES2022 Array#at がちょっとおかしい - Qiita / これは残念仕様😡 手放しに推奨するのは微妙 - yasu-log のブックマーク / はてなブックマーク

I guess we shouldn't use Array#at.
Array#atは使わないほうが良さそう - door-s-dev のブックマーク / はてなブックマーク

You can also see other responses from the following site, in Japanese.

The difference is only observable if you're passing something which is not an integral number. But if you've ended up with a non-integral number as an argument to .at, you almost certainly have a bug anyway.

This behavior is also the ecosystem precedent. Compare, for example, lodash's nth method, which works exactly the same way. I don't think this has ever actually caused problems in practice. (E.g., there don't seem to be any issues on the lodash repo expressing surprise at this behavior.)

1 Like

I don't know how many people have used _.nth, but I suspect it's a small percentage of JavaScript users. I've used lodash, but I didn't know that method existed.

Since there are users who are currently unhappy with Array#at, could you please think about making it more usable, instead of getting hung up on consistency for no reason?

1 Like

I'm sure there's a bias and I don't think it's enough people answering, but don't you think it's weird that the current spec makes misleading to such a large percentage of people?

4 Likes

OK. I've thought about it a lot, and it seems to be correct that a argument of a method named at should be rounded to an integer with ToIntegerOrInfinity, just like the similarly named String#charAt and String#charCodeAt. Indeed, it makes sense.

Syntax sugar for property access should be proposed in other ways.

1 Like

Since all of javascript numbers are floats, then they would be susceptible to rounding errors, so in some ways, it can actually be a good thing that it rounds.

const index = 0.3 + 0.3 + 0.3 + 0.1 // 0.99999
[1, 2, 3].at(index) // 1

I also agree that it should behave more like the slice() function. Yes, at() mimics array indexing in some ways, but it's also a function, not an operator, and so I would want it to be consistent with other functions.

In the end, we can disagree on this, but like has been previously pointed out, if you're trying to index with NaN, 'hello world', etc, then you've already got a bug in your program that needs to be sorted out. Fixing this type coercion issue with just a single function still leaves all of the other built in function susceptible to this kind of type coercion none-sense. The only real way to defend against it is to use something like typescript, not to try and change how the language operators after X point in time.

It looks plausible. However, in that code example, people might expect it to return 2 instead of 1. (Not that it matters, but the semicolon-less code apparently doesn't work as expected).

IMO, returning a seemingly correct value makes it harder to find bugs than returning nothing at all. And since it is impossible for TypeScript to determine whether a number is NaN or not, we have no way to fight it. In order to avoid creating bugs as much as possible, we should not rely on unstable methods such as Array#at. We should explicitly round numbers to integer values for property access.

const arr = [1, 2, 3];
const index = 0.3 + 0.3 + 0.3 + 0.1; // 0.99999

console.log(arr[Math.trunc(index)]); // 1
console.log(arr[Math.round(index)]); // 2

Whoops, you're right, it truncates, not rounds. I guess that's a pretty bad argument then :)

1 Like

Pardon me for jumping in, but a week has passed. Is there any progress?

What's being asked for here is a fundamental change to a stage 4 proposal that's already being shipped. The ship has already sailed. It's too late to fix this, even if we wanted to (which, not everyone seems to want this changed). People are already writing code that depends on this proposal.

The proposal is stage 4 and shipping in multiple browsers, there’s no changes that are likely to be made here.

It may be a more convenient method when it takes Object as indexable by keys rather than indexed numbers.

Why would that be convenient? The point of .at() is to be able to use negative and positive numbers interchangeably; for objects, there's no "wraparound" - just the key, or nothing, so obj[key] is more convenient than some kind of [obj.at](http://obj.at)(key) (setting aside that that would break with an object having a key of at.

1 Like

OK, I have seen the rationale.

I suddenly came up with an idea that we can create a new syntax like arr.[-1] to solve this problem. Is it reasonable to do so?

1 Like