Hone logo
Hone
Problems

TypeScript Typed Array Utilities

This challenge focuses on building robust and type-safe utility functions for working with arrays in TypeScript. You'll create functions that can safely transform and manipulate array elements while preserving and enforcing their types, making your code more predictable and reducing runtime errors.

Problem Description

Your task is to implement a set of generic TypeScript functions that operate on arrays. These functions should be designed to handle various common array operations, ensuring type safety throughout. Specifically, you need to implement the following:

  1. mapTyped<T, U>(array: T[], mapper: (item: T) => U): U[]: A function that maps over an array, applying a transformation function to each element and returning a new array of the transformed elements. The types of the original elements (T) and the transformed elements (U) should be clearly defined.

  2. filterTyped<T>(array: T[], predicate: (item: T) => boolean): T[]: A function that filters an array based on a predicate function. It should return a new array containing only the elements for which the predicate returns true. The type of the elements in the returned array should be the same as the input array.

  3. reduceTyped<T, U>(array: T[], reducer: (accumulator: U, currentValue: T) => U, initialValue: U): U: A function that reduces an array to a single value. It should accept a reducer function and an initial value for the accumulator. The types of the array elements (T) and the accumulator/result (U) should be distinct and properly handled.

  4. findTyped<T>(array: T[], predicate: (item: T) => boolean): T | undefined: A function that searches an array for the first element that satisfies a provided testing function. It should return the found element, or undefined if no element satisfies the test.

Key Requirements:

  • All functions must be generic and accept type parameters to define the types of array elements and, where applicable, the transformation or accumulator types.
  • The return type of each function must accurately reflect the types of its output.
  • The functions should not mutate the original arrays; they should always return new arrays (except for findTyped).

Expected Behavior:

  • mapTyped should behave like the built-in Array.prototype.map, but with explicit generic type arguments.
  • filterTyped should behave like the built-in Array.prototype.filter, but with explicit generic type arguments.
  • reduceTyped should behave like the built-in Array.prototype.reduce, but with explicit generic type arguments for both the array elements and the accumulator.
  • findTyped should behave like the built-in Array.prototype.find, but with explicit generic type arguments.

Edge Cases:

  • Empty input arrays.
  • Predicates/mappers that return null or undefined.
  • reduceTyped with an empty array and no initial value (though this specific implementation requires an initial value).

Examples

Example 1: mapTyped

const numbers = [1, 2, 3];
const stringifiedNumbers = mapTyped(numbers, (num) => `Number: ${num}`);
// Expected output: ["Number: 1", "Number: 2", "Number: 3"]
// Type of stringifiedNumbers: string[]

Example 2: filterTyped

const ages = [25, 17, 30, 15, 19];
const adults = filterTyped(ages, (age) => age >= 18);
// Expected output: [25, 30, 19]
// Type of adults: number[]

Example 3: reduceTyped

const prices = [10.5, 20.25, 5.0];
const total = reduceTyped(prices, (sum, price) => sum + price, 0);
// Expected output: 35.75
// Type of total: number

const words = ["hello", " ", "world"];
const sentence = reduceTyped(words, (acc, word) => acc + word, "");
// Expected output: "hello world"
// Type of sentence: string

Example 4: findTyped

const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 3, name: "Charlie" },
];
const bob = findTyped(users, (user) => user.name === "Bob");
// Expected output: { id: 2, name: "Bob" }
// Type of bob: { id: number; name: string; } | undefined

const nonExistent = findTyped(users, (user) => user.name === "David");
// Expected output: undefined
// Type of nonExistent: { id: number; name: string; } | undefined

Example 5: mapTyped with complex types

interface Person {
  firstName: string;
  lastName: string;
}

const people: Person[] = [
  { firstName: "Jane", lastName: "Doe" },
  { firstName: "John", lastName: "Smith" },
];

const fullNames = mapTyped(people, (person) => `${person.firstName} ${person.lastName}`);
// Expected output: ["Jane Doe", "John Smith"]
// Type of fullNames: string[]

Constraints

  • The input array for all functions will be a valid JavaScript array.
  • The mapper, predicate, and reducer functions provided as arguments will be valid JavaScript functions.
  • initialValue for reduceTyped will be provided.
  • Performance is not a critical concern for this challenge; focus on correctness and type safety.

Notes

  • Remember to utilize TypeScript's generic type parameters effectively to infer and enforce types.
  • Consider the return type of findTyped carefully, as it might not find a matching element.
  • This challenge is designed to reinforce your understanding of generics, function signatures, and type inference in TypeScript.
Loading editor...
typescript