#How to filter an array of union generic type

14 messages · Page 1 of 1 (latest)

stoic flax
#
function apply<T extends Fruit[] | Vegetable[]>(all: T): T {
  return all.filter((x) => true);
}

So I want the parameter all to either be of type Fruit[] or Vegetable[], not (Fruit | Vegetable)[]. And I want the function to return the same type as all. I think this confuses TS or I'm the one getting confused.

I'm getting the error This expression is not callable. Each member of the union type '...' has signatures, but none of those signatures are compatible with each other..

Is there another way of doing this? Like overloading or something?

edgy jacinth
#

@stoic flax I think you should do:

function apply<T extends Fruit | Vegetable>(all: T[]): T[] {
  return all.filter((x) => true);
}
#

There are some issues with functions on unions of arrays... but I don't think that's the core issue here.

#

The current signature claims it puts out basically the same array that goes in, which isn't true:

apply<[Fruit, Fruit]>([apple, orange]); // Type claims the result should be [Fruit, Fruit]
stoic flax
#

as in, (Fruit | Vegetable)[], which I'm trying to avoid

edgy jacinth
#

Yeah, but I don't think there's a way to avoid that.

#

Well, at least not with generics.

#

I guess you could overload, like you said:

#
function apply(all: Fruit[]): Fruit[];
function apply(all: Vegetable[]): Vegetable[];
function apply(all: Array<Fruit | Vegetable>): Array<Fruit | Vegetable> {
  return all.filter((x) => true);
}
#

I'm hesitant to reach for overloads because they're not safe, though.

stoic flax
#

thanks, the first solution looks good enough

jade garnet
#

The problem is that filter is being overly opinionated and extremely cautious. It recognizes that all could be Fruits OR Vegetables but has no way to verify the consistency of the array at runtime. If you add a typeguard, you can make it work the way you want.

function isFruits(
    produce: Array<Fruit | Vegetable>
): produce is Array<Fruit> {
    return true;
}

function apply<T extends Fruit[] | Vegetable[]>(all: T) {
    const produceFilter = (produce: Fruit | Vegetable) => true

    return isFruits(all) ? all.filter(produceFilter) : all.filter(produceFilter);
}
stoic flax
#

thanks both of you, I ended up changing the code a bit such that I don't need to use this