#Filter sum types based on property

20 messages · Page 1 of 1 (latest)

grave pendant
#
type Candy =
  | { name: "Skittles"; type: "Regular" | "Tropical" }
  | { name: "Black Licorice"; qty: number }
  | { name: "Runts"; isBanana: boolean };

Is there anyway to construct a type that accepts a sum type of that nature and allows you to filter based on a property, in this case name, so only include Skittles and Runts?

round blade
#

a sum type?

#

you mean a union?

#
type Filter<Object extends Record<PropertyKey, any>, FilterKey extends keyof Object, AcceptedValue> =
  Object extends any
  ? Object[FilterKey] extends AcceptedValue
    ? Object
    : never
  : never;

type Candy =
  | { name: "Skittles"; type: "Regular" | "Tropical" }
  | { name: "Black Licorice"; qty: number }
  | { name: "Runts"; isBanana: boolean };

type FilteredCandy = Filter<Candy, "name", "Runts" |  "Skittles">;
declare const fc: FilteredCandy;
fc.name;
// ^?
#

!ts

quick dockBOT
#
type Filter<Object extends Record<PropertyKey, any>, FilterKey extends keyof Object, AcceptedValue> =
  Object extends any
  ? Object[FilterKey] extends AcceptedValue
    ? Object
    : never
  : never;

type Candy =
  | { name: "Skittles"; type: "Regular" | "Tropical" }
  | { name: "Black Licorice"; qty: number }
  | { name: "Runts"; isBanana: boolean };

type FilteredCandy = Filter<Candy, "name", "Runts" |  "Skittles">;
declare const fc: FilteredCandy;
fc.name;
// ^? - (property) name: "Runts" | "Skittles"
grave pendant
#

Pog I thought I would have to change how i stored the data this is amazing

#

Thank you

#

!close

round blade
#

also it's conditional types 101

#

here is the doc if you're interested

#

the only "trick" here was that you had an union, so you had to use distributive conditional types (the extra Object extends any ? : never wrapping the whole type)

#

@grave pendant

marble veldt
#

you can also do this with a plain Extract

quick dockBOT
#
That_Guy977#5882

Preview:```ts
type Candy =
| {name: "Skittles"; type: "Regular" | "Tropical"}
| {name: "Black Licorice"; qty: number}
| {name: "Runts"; isBanana: boolean}

type Filtered = Extract<
Candy,
{name: "Skittles" | "Runts"}

// ^?
type FilteredName = Filtered["name"]
...```

round blade
#

Extract is just a shorthand, but it's always nice to know how it works under the hood

marble veldt
#

sure but it's more powerful and flexible for this kind of thing, why not just recommend that as the primary solution