pbeshai / tidy

Tidy up your data with JavaScript, inspired by dplyr and the tidyverse
https://pbeshai.github.io/tidy
MIT License
725 stars 21 forks source link

Allow type guards for filter #39

Closed iimog closed 3 years ago

iimog commented 3 years ago

A common use case for me is filtering possibly null values before mutating data. For example like this:

import { filter, mutate, tidy } from "@tidyjs/tidy";

type Data = { a: number | null };
const data: Array<Data> = [{ a: null }, { a: 1 }, { a: null }, { a: 2 }];

tidy(
  data,
  filter((x) => x.a !== null),
  mutate({
    b: (x) => 2 * x.a
  })
);

Of course typescript does not know that after filtering x.a can not be null so it complains: Object is possibly 'null'. This is fully expected and I love that tidyjs has such great type inference (I hope you do not make it dumber as suggested in #14).

A solution to my problem would be a type guard (thanks to @phryneas for the suggestion):

tidy(
    data,
    filter((x): x is Data & { a: number } => x.a !== null),
    mutate({
        b: (x) => 2 * x.a
    })
);

However this requires an additional declaration for the filter function that allows for type guards (it can be added without interference with the existing functionality). I currently do this:

declare module "@tidyjs/tidy" {
  function filter<T extends object, O extends T>(
    filterFn: (item: T, index: number, array: T[]) => item is O
  ): TidyFn<T, O>;
}

I'll send a pull request that adds this declaration to the library itself.

pbeshai commented 3 years ago

ooo cool, I have yet to use type guards. This sounds good to me. Let me know if you see other ways we can improve the types!

pbeshai commented 3 years ago

Fixed via #40 in v2.4.1