📚 4 min read
Array Operations in TypeScript ​
This section covers type-safe array operations and utility functions for array manipulation.
Overview ​
TypeScript provides powerful type checking for array operations. This guide covers common patterns and utilities for working with arrays in a type-safe manner.
Type-Safe Array Methods ​
Filter with Type Predicates ​
typescript
// Type predicate for non-null values
function isNonNull<T>(value: T | null | undefined): value is T {
return value !== null && value !== undefined;
}
// Usage
const items: (string | null)[] = ['a', null, 'b', undefined, 'c'];
const nonNullItems: string[] = items.filter(isNonNull);
Map with Type Inference ​
typescript
// Type-safe mapper
function mapToNumbers<T>(array: T[], mapper: (item: T) => number): number[] {
return array.map(mapper);
}
// Usage
const strings = ['1', '2', '3'];
const numbers = mapToNumbers(strings, (str) => parseInt(str, 10));
Reduce with Accumulator Types ​
typescript
interface GroupedItems<T> {
[key: string]: T[];
}
function groupBy<T>(items: T[], keyFn: (item: T) => string): GroupedItems<T> {
return items.reduce((acc, item) => {
const key = keyFn(item);
return {
...acc,
[key]: [...(acc[key] || []), item],
};
}, {} as GroupedItems<T>);
}
Array Utility Functions ​
Safe Array Access ​
typescript
function safeGet<T>(array: T[], index: number, defaultValue: T): T {
return index >= 0 && index < array.length ? array[index] : defaultValue;
}
// Usage
const arr = [1, 2, 3];
const value = safeGet(arr, 5, 0); // Returns 0
Chunk Array ​
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[][]);
}
// Usage
const items = [1, 2, 3, 4, 5];
const chunks = chunk(items, 2); // [[1, 2], [3, 4], [5]]
Unique Values ​
typescript
function unique<T>(array: T[]): T[] {
return Array.from(new Set(array));
}
// With custom comparator
function uniqueBy<T>(array: T[], comparator: (a: T, b: T) => boolean): T[] {
return array.reduce((uniques, item) => {
const exists = uniques.some((unique) => comparator(unique, item));
return exists ? uniques : [...uniques, item];
}, [] as T[]);
}
Type-Safe Array Transformations ​
Tuple Types ​
typescript
type Tuple<T, N extends number> = N extends N
? number extends N
? T[]
: _TupleOf<T, N, []>
: never;
type _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N
? R
: _TupleOf<T, N, [T, ...R]>;
// Usage
function createArray<T>(value: T, length: number): T[] {
return Array.from({ length }, () => value);
}
function createTuple<T, N extends number>(value: T, length: N): Tuple<T, N> {
return createArray(value, length) as Tuple<T, N>;
}
// Type-safe fixed-length array
const tuple = createTuple('x', 3); // type is [string, string, string]
Array Element Types ​
typescript
type ArrayElement<T> = T extends (infer U)[] ? U : never;
// Usage
type Numbers = number[];
type Number = ArrayElement<Numbers>; // type is number
type Mixed = (string | number)[];
type MixedElement = ArrayElement<Mixed>; // type is string | number
Best Practices ​
Type Guards with Arrays:
typescriptfunction isArrayOfType<T>( value: unknown, typeGuard: (item: unknown) => item is T ): value is T[] { return Array.isArray(value) && value.every((item) => typeGuard(item)); }
Immutable Array Operations:
typescriptfunction insertAt<T>(array: readonly T[], index: number, item: T): T[] { return [...array.slice(0, index), item, ...array.slice(index)]; }
Type-Safe Array Sorting:
typescriptfunction typeSafeSort<T>( array: T[], compareFn: (a: T, b: T) => number ): T[] { return [...array].sort(compareFn); }
Real-World Example ​
typescript
interface User {
id: number;
name: string;
role: 'admin' | 'user';
lastActive?: Date;
}
class UserCollection {
private users: User[] = [];
add(user: User): void {
this.users.push(user);
}
findById(id: number): User | undefined {
return this.users.find((user) => user.id === id);
}
filterByRole(role: User['role']): User[] {
return this.users.filter((user) => user.role === role);
}
getActiveUsers(): User[] {
const now = new Date();
const dayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);
return this.users.filter(
(user) => user.lastActive && user.lastActive > dayAgo
);
}
groupByRole(): Record<User['role'], User[]> {
return groupBy(this.users, (user) => user.role);
}
sortByLastActive(): User[] {
return typeSafeSort(this.users, (a, b) => {
const dateA = a.lastActive?.getTime() ?? 0;
const dateB = b.lastActive?.getTime() ?? 0;
return dateB - dateA;
});
}
}