Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(maybe/find): type narrowing via predicates #299

Merged
merged 2 commits into from
Mar 7, 2022

Conversation

buschtoens
Copy link
Contributor

@buschtoens buschtoens commented Jan 25, 2022

Adds support for type narrowing via type predicates to find.

interface User {
  name: string;
  role: string;
}

interface Admin extends User {
  role: 'admin';
}

const isAdmin = (user: User): user is Admin => user.role === 'admin';

const users = [
  { name: 'Jan',   role: 'contributor' },
  { name: 'Chris', role: 'admin'       },
];

const admin = find(isAdmin, users);
// => Maybe<Admin>

const oldApproach = find(user => user.role === 'admin', users);
// => Maybe<User>

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks so much for this PR, and for using True Myth!
This repo is actively supported, but maintainers are busy. One of us will be by soon to give it a look. If you don't hear from us within a week, feel free to give us a gentle nudge.

@buschtoens
Copy link
Contributor Author

buschtoens commented Jan 25, 2022

I was a bit thrown off by these things:

  • The find test is nested in deprecated toolbox/array re-exports.
    describe('deprecated toolbox/array re-exports', () => {
    test('`find`', () => {
  • Does this mean it's supposed to be removed? There is no @deprecated tag indicating so.

    true-myth/src/maybe.ts

    Lines 1114 to 1169 in 10c73a5

    // NOTE: documentation is lightly adapted from the MDN and TypeScript docs for
    // `Array.prototype.find`.
    /**
    Safely search for an element in an array.
    This function behaves like `Array.prototype.find`, but returns `Maybe<T>`
    instead of `T | undefined`.
    ## Examples
    The basic form is:
    ```ts
    import Maybe from 'true-myth/maybe';
    let array = [1, 2, 3];
    Maybe.find(v => v > 1, array); // Just(2)
    Maybe.find(v => v < 1, array); // Nothing
    ```
    The function is curried so you can use it in a functional chain. For example
    (leaving aside error handling on a bad response for simplicity), suppose the
    url `https://arrays.example.com` returned a JSON payload with the type
    `Array<{ count: number, name: string }>`, and we wanted to get the first
    of these where `count` was at least 100. We could write this:
    ```ts
    import Maybe from 'true-myth/maybe';
    type Item = { count: number; name: string };
    type Response = Array<Item>;
    // curried variant!
    const findAtLeast100 = Maybe.find(({ count }: Item) => count > 100);
    fetch('https://arrays.example.com')
    .then(response => response.json() as Response)
    .then(findAtLeast100)
    .then(found => {
    if (found.isJust()) {
    console.log(`The matching value is ${found.value.name}!`);
    }
    });
    ```
    @param predicate A function to execute on each value in the array, returning
    `true` when the item in the array matches the condition. The
    signature for `predicate` is identical to the signature for
    the first argument to `Array.prototype.find`. The function
    is called once for each element of the array, in ascending
    order, until it finds one where predicate returns true. If
    such an element is found, find immediately returns that
    element value wrapped in `Just`. Otherwise, `Maybe.find`
    returns `Nothing`.
    * @param array The array to search using the predicate.
    */
  • If so, why & what is the recommended replacement? To reduce API surface area & maintenance burden?
  • The documentation suggests that it's available as a static method Maybe.find.

    true-myth/src/maybe.ts

    Lines 1127 to 1130 in 10c73a5

    import Maybe from 'true-myth/maybe';
    let array = [1, 2, 3];
    Maybe.find(v => v > 1, array); // Just(2)

    But it actually has to be imported as an individual function.
    import { find } from 'true-myth/maybe';

@chriskrycho
Copy link
Collaborator

  1. Thanks for this! I’ll try to review it this week (probably Friday, when I have some investment time available). In principle, I certainly support the idea!

  2. On the questions—I think I may have ended up with some things in a quirky spot internally as part of the extraction of the branch for v6 (v6.0.0 (targeting May 2022) #244). 🤔 The goal (per Introduce true-myth/toolbelt module for better tree shaking #246) is to make the library more properly tree-shakeable when we cut that v6 in a few months, by decoupling the Maybe and Result modules from each other.

@buschtoens
Copy link
Contributor Author

Awesome! Thanks for getting back to me so swiftly and answering my questions.

@chriskrycho
Copy link
Collaborator

(Haven’t forgotten about this, just slammed! Will get back to it!)

Copy link
Collaborator

@chriskrycho chriskrycho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some naming tweaks to match the rest of the internals.

src/maybe.ts Outdated Show resolved Hide resolved
src/maybe.ts Outdated Show resolved Hide resolved
src/maybe.ts Outdated Show resolved Hide resolved
@chriskrycho
Copy link
Collaborator

This looks good—once it's ✅ I'll merge it and cut a 5.2.0 release with it. I'll also port it forward to the v6 branch later this week (or, if you get time, feel free to do so!).

@chriskrycho
Copy link
Collaborator

Windows CI failure is a spurious network issue (GitHub’s CI boxes seem to be having regular problems with this). Merging!

@chriskrycho chriskrycho merged commit 484432b into true-myth:main Mar 7, 2022
@buschtoens buschtoens deleted the find-type-narrowing branch March 7, 2022 16:04
@buschtoens
Copy link
Contributor Author

Fantastic! Thanks for merging. 😊

If I can find the time and remember to do so, I'll open a PR for next as well. No promises though. 😅

@chriskrycho
Copy link
Collaborator

All good! It's out now as 5.2.0, and whichever of us gets to porting it to next first wins… some kind of prize.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants