Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b29b484

Browse files
acustileebyron
authored andcommitted
Make isArrayLike check more precise to avoid false positives (immutable-js#1520)
* Update isArrayLike check to be more precise Verify that a value is array-like by checking for a numeric length and by confirming that it has the length - 1 key set (even if it is undefined). If length is 0, then check that it doesn’t have any other properties other than length * Array-likes may also have additional non-numeric keys * Handle length === 0 edge case * Ensure integer lengths * Format
1 parent 5726bd1 commit b29b484

File tree

4 files changed

+32
-9
lines changed

4 files changed

+32
-9
lines changed

__tests__/List.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ describe('List', () => {
5858
});
5959

6060
it('accepts an array-like', () => {
61-
const v = List({ length: 3, 1: 'b' } as any);
62-
expect(v.get(1)).toBe('b');
63-
expect(v.toArray()).toEqual([undefined, 'b', undefined]);
61+
const v = List({ length: 3, 2: 'c' } as any);
62+
expect(v.get(2)).toBe('c');
63+
expect(v.toArray()).toEqual([undefined, undefined, 'c']);
6464
});
6565

6666
it('accepts any array-like collection, including strings', () => {

__tests__/Seq.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,19 @@ describe('Seq', () => {
4747
});
4848

4949
it('accepts an array-like', () => {
50-
const alike: any = { length: 2, 0: 'a', 1: 'b' };
51-
const seq = Seq(alike);
50+
const seq = Seq({ length: 2, 0: 'a', 1: 'b' });
5251
expect(isIndexed(seq)).toBe(true);
5352
expect(seq.size).toBe(2);
5453
expect(seq.get(1)).toBe('b');
54+
55+
const map = Seq({ length: 1, foo: 'bar' });
56+
expect(isIndexed(map)).toBe(false);
57+
expect(map.size).toBe(2);
58+
expect(map.get('foo')).toBe('bar');
59+
60+
const empty = Seq({ length: 0 });
61+
expect(isIndexed(empty)).toBe(true);
62+
expect(empty.size).toEqual(0);
5563
});
5664

5765
it('does not accept a scalar', () => {

__tests__/Set.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ describe('Set', () => {
2020
});
2121

2222
it('accepts array-like of values', () => {
23-
const s = Set<any>({ length: 3, 1: 2 } as any);
23+
const s = Set<any>({ length: 3, 2: 3 } as any);
2424
expect(s.size).toBe(2);
2525
expect(s.has(undefined)).toBe(true);
26-
expect(s.has(2)).toBe(true);
27-
expect(s.has(1)).toBe(false);
26+
expect(s.has(3)).toBe(true);
27+
expect(s.has(2)).toBe(false);
2828
});
2929

3030
it('accepts string, an array-like collection', () => {

src/utils/isArrayLike.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,20 @@
66
*/
77

88
export default function isArrayLike(value) {
9-
return value && typeof value.length === 'number';
9+
if (Array.isArray(value) || typeof value === 'string') {
10+
return true;
11+
}
12+
13+
return (
14+
value &&
15+
typeof value === 'object' &&
16+
Number.isInteger(value.length) &&
17+
value.length >= 0 &&
18+
(value.length === 0
19+
? // Only {length: 0} is considered Array-like.
20+
Object.keys(value).length === 1
21+
: // An object is only Array-like if it has a property where the last value
22+
// in the array-like may be found (which could be undefined).
23+
value.hasOwnProperty(value.length - 1))
24+
);
1025
}

0 commit comments

Comments
 (0)