📚 5 min read
Array Utilities in TypeScript ​
This section provides a collection of type-safe array utility functions and patterns for common array operations.
Overview ​
Array utilities help you perform common array operations in a type-safe manner while maintaining code readability and reusability.
Basic Utilities ​
Array Creation ​
typescript
function createArray<T>(length: number, defaultValue: T): T[] {
return Array(length).fill(defaultValue);
}
function range(start: number, end: number): number[] {
return Array.from({ length: end - start }, (_, i) => start + i);
}
function unique<T>(array: T[]): T[] {
return Array.from(new Set(array));
}
// Usage
const zeros = createArray(5, 0); // [0, 0, 0, 0, 0]
const numbers = range(1, 5); // [1, 2, 3, 4]
const uniqueValues = unique([1, 2, 2, 3, 3, 4]); // [1, 2, 3, 4]
Array Manipulation ​
typescript
function chunk<T>(array: T[], size: number): T[][] {
return array.reduce((chunks, item, index) => {
const chunkIndex = Math.floor(index / size);
if (!chunks[chunkIndex]) {
chunks[chunkIndex] = [];
}
chunks[chunkIndex].push(item);
return chunks;
}, [] as T[][]);
}
function shuffle<T>(array: T[]): T[] {
return [...array].sort(() => Math.random() - 0.5);
}
function rotate<T>(array: T[], offset: number): T[] {
const normalizedOffset = offset % array.length;
return [
...array.slice(normalizedOffset),
...array.slice(0, normalizedOffset),
];
}
// Usage
const chunks = chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
const shuffled = shuffle([1, 2, 3, 4, 5]); // Random order
const rotated = rotate([1, 2, 3, 4, 5], 2); // [3, 4, 5, 1, 2]
Advanced Utilities ​
Array Type Checking ​
typescript
function isArrayOf<T>(
array: unknown,
typeGuard: (item: unknown) => item is T
): array is T[] {
return Array.isArray(array) && array.every(typeGuard);
}
function hasMinLength<T>(array: T[], minLength: number): array is [T, ...T[]] {
return array.length >= minLength;
}
function isUnique<T>(array: T[]): boolean {
return new Set(array).size === array.length;
}
// Usage
const numbers = [1, 2, 3];
if (isArrayOf(numbers, (x): x is number => typeof x === 'number')) {
// numbers is type number[]
}
const nonEmpty = [1, 2, 3];
if (hasMinLength(nonEmpty, 1)) {
// nonEmpty is type [number, ...number[]]
}
console.log(isUnique([1, 2, 3])); // true
console.log(isUnique([1, 2, 2, 3])); // false
Array Transformations ​
typescript
function groupBy<T, K extends string | number | symbol>(
array: T[],
getKey: (item: T) => K
): Record<K, T[]> {
return array.reduce(
(groups, item) => {
const key = getKey(item);
groups[key] = groups[key] || [];
groups[key].push(item);
return groups;
},
{} as Record<K, T[]>
);
}
function partition<T>(array: T[], predicate: (item: T) => boolean): [T[], T[]] {
return array.reduce(
([pass, fail], item) => {
return predicate(item)
? [[...pass, item], fail]
: [pass, [...fail, item]];
},
[[], []] as [T[], T[]]
);
}
function zip<T, U>(first: T[], second: U[]): [T, U][] {
const length = Math.min(first.length, second.length);
return Array.from({ length }, (_, i) => [first[i], second[i]]);
}
// Usage
const users = [
{ id: 1, role: 'admin' },
{ id: 2, role: 'user' },
{ id: 3, role: 'admin' },
];
const byRole = groupBy(users, (user) => user.role);
// {
// admin: [{ id: 1, role: 'admin' }, { id: 3, role: 'admin' }],
// user: [{ id: 2, role: 'user' }]
// }
const [admins, nonAdmins] = partition(users, (user) => user.role === 'admin');
const pairs = zip([1, 2, 3], ['a', 'b', 'c']); // [[1, 'a'], [2, 'b'], [3, 'c']]
Real-World Example ​
typescript
// Array utility class with common operations
class ArrayUtils<T> {
constructor(private readonly items: T[]) {}
// Basic operations
get length(): number {
return this.items.length;
}
toArray(): T[] {
return [...this.items];
}
// Transformations
map<U>(fn: (item: T) => U): ArrayUtils<U> {
return new ArrayUtils(this.items.map(fn));
}
filter(predicate: (item: T) => boolean): ArrayUtils<T> {
return new ArrayUtils(this.items.filter(predicate));
}
// Aggregations
reduce<U>(fn: (acc: U, item: T) => U, initial: U): U {
return this.items.reduce(fn, initial);
}
// Utility methods
chunk(size: number): ArrayUtils<T[]> {
return new ArrayUtils(chunk(this.items, size));
}
shuffle(): ArrayUtils<T> {
return new ArrayUtils(shuffle(this.items));
}
unique(comparator?: (a: T, b: T) => boolean): ArrayUtils<T> {
if (comparator) {
return new ArrayUtils(
this.items.filter(
(item, index) =>
this.items.findIndex((other) => comparator(item, other)) === index
)
);
}
return new ArrayUtils(unique(this.items));
}
groupBy<K extends string | number | symbol>(
getKey: (item: T) => K
): Record<K, T[]> {
return groupBy(this.items, getKey);
}
partition(predicate: (item: T) => boolean): [ArrayUtils<T>, ArrayUtils<T>] {
const [pass, fail] = partition(this.items, predicate);
return [new ArrayUtils(pass), new ArrayUtils(fail)];
}
// Statistics (for numeric arrays)
sum(this: ArrayUtils<number>): number {
return this.items.reduce((sum, n) => sum + n, 0);
}
average(this: ArrayUtils<number>): number {
if (this.length === 0) throw new Error('Cannot average empty array');
return this.sum() / this.length;
}
min(this: ArrayUtils<number>): number {
if (this.length === 0) throw new Error('Cannot get min of empty array');
return Math.min(...this.items);
}
max(this: ArrayUtils<number>): number {
if (this.length === 0) throw new Error('Cannot get max of empty array');
return Math.max(...this.items);
}
}
// Usage example
interface User {
id: number;
name: string;
age: number;
role: 'admin' | 'user';
}
const users: User[] = [
{ id: 1, name: 'John', age: 30, role: 'admin' },
{ id: 2, name: 'Jane', age: 25, role: 'user' },
{ id: 3, name: 'Bob', age: 35, role: 'admin' },
{ id: 4, name: 'Alice', age: 28, role: 'user' },
];
const utils = new ArrayUtils(users);
// Get unique roles
const roles = utils
.map((user) => user.role)
.unique()
.toArray();
// Group users by role
const usersByRole = utils.groupBy((user) => user.role);
// Get admin and non-admin users
const [admins, nonAdmins] = utils.partition((user) => user.role === 'admin');
// Get average age
const averageAge = utils.map((user) => user.age).average();
// Get users in random order, chunked by 2
const randomPairs = utils.shuffle().chunk(2).toArray();
console.log({
roles,
usersByRole,
adminCount: admins.length,
nonAdminCount: nonAdmins.length,
averageAge,
randomPairs,
});
Best Practices ​
Type Safety:
- Use generic type parameters
- Implement proper type guards
- Validate array contents
Performance:
- Minimize array copies
- Use appropriate data structures
- Consider lazy evaluation
Reusability:
- Create composable utilities
- Document edge cases
- Handle error conditions