-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Check index array length #38000
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
let a = []
a[1000] = 1
a.length // 1001 JS AWESOME🤷🏻♂️ |
We go into this a bit at #13778. There isn't a huge amount useful that can be done here while still being sound given the existence of things like |
Assuming that tuples are still a nice addition to the language, we could leverage them to provide some incremental index safety here, e.g.:
So the idea would be:
As an alternative, we could do this only for immutable arrays/tuples. You can't do Another alternative is to drop 4 (which can still be implemented with custom type guards), but at least implement 3, so we can do:
|
I would definitely find even just basic cases working better than nothing. At current I use a custom helper that takes an array and returns if it's a tuple of a given length e.g.: function isLength<T>(arr: T[], length: 0): arr is [];
function isLength<T>(arr: T[], length: 1): arr is [T];
function isLength<T>(arr: T[], length: 2): arr is [T, T];
// etc
function isLength<T>(arr: T[], length: number): arr is T[] {
return arr.length === length;
} I use it quite a lot as I'm also using |
@Jamesernator I havent checked your sample but from what I see you cannot guarantee that array has at least 1 element, right? So your method should be named 'exactLength', instead of 'atLeast' |
This is more complex unfortunately You can do this:
And yet...
|
I understand the complexity for mutable arrays. But what about One issue is that mutable arrays (potentially sparse by accident) are automatically converted to immutable, so this code is still problematic: function firstElement<T>(x: readonly T[]): T {
if (x.length === 0) throw new Error('Invalid argument');
return x[0]; // Seems safe, we have checked length, and it's immutable.
}
let arr = [];
arr[2] = 1;
firstElement(arr); // Should not return undefined However, given the amount of code I've seen that uses if (x.length > 0) return x[0]!; // The compiler doesn't realize I already checked the length??? I'd say One possibility to bring this feature to function notSparse<T>(arr: ReadonlyArray<T|undefined>): arr is ReadonlyArray<T> {
return arr.every(x => x !== undefined);
}
// This is fine, Array<T> can be assigned to ReadonlyArray<T|undefined> acknowledging that
// it is potentially sparse.
if (notSparse(arr)) {
firstElement(arr);
} But this is a breaking change (needs a flag, etc). Of course, a better solution would be to change JavaScript to add non-sparse arrays to the standard (even if it's just a patch, similar to Alternatively, TypeScript could bridge the gap, with proposals like #24752. Although I acknowledge that this complicates the compiler even more, as it would have to check boundaries for both reads and writes, in subtly different ways. In the meantime, the pattern I've developed with if (x.length > 0) return x[0]!; // The compiler doesn't realize I already checked the length??? With this: const first = x[0];
if (first !== undefined) return first; No need to shut down the type checker, or use the |
@rubenlg just because the variable you have has type readonlyarray doesn't mean that someone else doesn't have a reference to the same value with a mutable type of binding, So the readonlyness of array tells you nothing about whether or not it's mutable in practice |
@RyanCavanaugh True, and that argument applies to objects as well, not just arrays. For example: interface Mutable {
x?: number;
}
interface Immutable {
readonly x?: number;
}
function consumeNumber(x: number) {
if (x === undefined) throw new Error('Undefined');
}
const MUTABLE: Mutable = {
x: 2
};
const READONLY: Immutable = MUTABLE;
if (READONLY.x) {
MUTABLE.x = undefined;
consumeNumber(READONLY.x);
} In both cases (objects and arrays), the automatic conversion from mutable to readonly is problematic. But in the case of objects, Typescript favors convenience, pretending the developer knows what they are doing when they declare it as inmutable. |
The above discussion makes sense as to why the length check is not enough for type narrowing, but what about when the index is known to contain a non null object because it has been retrieved from findIndex such as:
Although this can be easily replaced with this which does not throw any error, I am just curious as to why the first example does not work:
|
@MDanialSaleem Several reasons:
So in the end, it has the exact same problems as doing a bounds check on any other index. Here is an example in which your first code snippet could have a bug, and the compiler is catching it: const x: number[] = new Array(10); // Sparse array, all 10 elements are undefined
const index = x.findIndex((x, i) => i == 2); // returns 2
console.log(x[index].toFixed(2)); // Typescript complains, and it's actually catching a bug Of course, you can write other callbacks for |
Thanks @rubenlg for the detailed answer.
Yes, I was referring specifically to this case. So TS cannot, right now, infer if the parameter passed to a function is not null by statically analyzing the body of the function, but can it not be implemented, potentially using the same mechanism that TS uses to infer return types? Although, I do not think there will be many valid usecases of this, considering that the second example in my original question is a very valid way of doing things. |
This issue has been marked as "Too Complex" and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
If/once the record and tuple support lands, narrowing based on a native tuple length should become possible. |
@RyanCavanaugh I think @rubenlg made good points that weren't answered. Namely, why are restrictions imposed on arrays that are not imposed on tuples or objects? |
Trade-offs can differ in their weight by scenario. Arrays, for example, have many built-in methods that mutate them; objects do not. |
And what do you think about something like what @jcalz suggested here: (I just left a comment at the end of that thread as well). |
Search Terms
Indexing, Array, Strict check
Suggestion
Strict check to indexing a array
Use Cases
Safety with arrays usages
Examples
Checklist
My suggestion meets these guidelines:
The text was updated successfully, but these errors were encountered: