OliverRadini
OliverRadini

Reputation: 6476

Avoiding any type when filtering and (hopefully) casting

When I define the following function:

export const filterAndCast = <T, U>(
    items: T[],
    predicate: Predicate<T>,
    cast: (x: T) => U,
) => items
    .reduce(
        (p, c) => [
            ...p,
            ...(predicate(c) ? [cast(c)] : []),
        ],
        [],
    );

I'd expect to have U[] returned. But I instead get any[].

How can I fix this?

Upvotes: 0

Views: 36

Answers (2)

Grabofus
Grabofus

Reputation: 2054

You just need to implicitly type the generic param of reduce().

// Assuming your predicate is this
type Predicate<T> = (x: T) => boolean;

export const filterAndCast = <T, U>(
    items: T[],
    predicate: Predicate<T>,
    cast: (x: T) => U
) => items
    .reduce<U[]>( // Provide <U[]> here
        (p, c) => [
            ...p,
            ...(predicate(c) ? [cast(c)] : []),
        ],
        [],
    );

// Keep numbers or string that can be casted to numbers
const results = filterAndCast(
    [1, 3, 'asd', '1', '2', '3'],
    (x) =>
        (typeof x === 'number') ||
        (typeof x === 'string' && !isNaN(parseFloat(x))),
    (x) => typeof x === 'number' ? x : parseFloat(x)
);

As a side note you could refactor the reduce(), so it only spreads the array when the predicate returns true.

items.reduce<U[]>((p, c) => (predicate(c) ? [...p, cast(c)] : p), [])

Upvotes: 1

koalaok
koalaok

Reputation: 5760

How about this?

export const filterAndCast = <T, U>(
    items: T[],
    predicate: (c:T) => boolean,
    cast: (x: T) => U,
) => items
    .reduce(
        (p, c) => [
            ...p,
            ...(predicate(c) ? [cast(c)] : []),
        ],
        [],
    );



const results = filterAndCast([1, 2, 3], x => x % 2 === 0, x => Number(x))


results.forEach(el=> console.log(typeof el));

Upvotes: 1

Related Questions