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

Add type safety for purry #560

Open
somnicattus opened this issue Mar 7, 2024 · 7 comments
Open

Add type safety for purry #560

somnicattus opened this issue Mar 7, 2024 · 7 comments
Labels
feature request New feature or request typing Something type related

Comments

@somnicattus
Copy link
Contributor

somnicattus commented Mar 7, 2024

How do you think about this?

Lazy is ignored because I don't know much about it.

concerns

It seems work well with non-generic function types, but not with generic function types... :(

implementations

/** Parameters type when the given Function type is purried. */
export type PurriedParameters<
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    F extends (...args: readonly any[]) => unknown,
> =
    Parameters<F> extends readonly [unknown, ...infer R extends unknown[]]
        ? Parameters<F> | R
        : never;
/** ReturnType when the given Function type is purried and called with the given Parameters type.*/
export type PurriedReturnType<
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    F extends (...args: readonly any[]) => unknown,
    P extends PurriedParameters<F>,
> =
    Parameters<F> extends readonly [infer T, ...unknown[]]
        ? P extends Parameters<F>
            ? ReturnType<F>
            : (value: T) => ReturnType<F>
        : never;

export function purry<
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    F extends (...args: readonly any[]) => ReturnType<F>,
    P extends PurriedParameters<F>,
>(fn: F, args: P): PurriedReturnType<F, P>

usage

function pow<P extends PurriedParameters<typeof Math.pow>>(
    ...args: P
): PurriedReturnType<typeof Math.pow, P> {
    return purry(Math.pow, args);
}
@eranhirsch
Copy link
Collaborator

eranhirsch commented Mar 7, 2024

We don't really need a return type for purry because we instead rely on function overloads to define the purried overloaded type (which means purry is only used as an "implementation detail").

For external usage, purry as a tool, I'd still recommend people implement helper functions the same way we do.

So this brings me up to the question of whether a typed purry replaces the way we implement our functions substantially, removing the need to declare overloads to begin with and only typing the dataFirst impl and getting everything else inferred automatically. To answer that question, I think the main problem would be how do we handle generic functions, those that take a <T> for a param. Can you test to see if, for example, this purried type would work with map or filter? Does it still maintain the generic throughout the type?

e.g.

declare function map<T, S>(array: T[], mapper: (item: T) => S): S[];

function purriedMap(...args: PurriedParameters<typeof map>): PurriedReturnType<typeof map> {
  return purry(map, args);
}

// Is x typed properly here?
const result = purriedMap([1,2,3], (x) => x + 1);

// what about here?
const result = pipe([1,2,3], purriedMap((x) => x + 1));

@somnicattus
Copy link
Contributor Author

But purry is exported and user can optionally use purry for their own functions.

@somnicattus
Copy link
Contributor Author

somnicattus commented Mar 7, 2024

I updated the code on the top. How about this?

Maybe no overload is needed with this.

@somnicattus
Copy link
Contributor Author

somnicattus commented Mar 7, 2024

I think PurriedReturnType is somehow not working as PurriedParameters generic with generic function types.

But it would be still useful to support type checking for overloading even if PurriedReturnType was just union type.

export type PurriedReturnType<
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    F extends (...args: readonly any[]) => unknown,
> =
    Parameters<F> extends readonly [infer T, ...unknown[]]
        ? ReturnType<F> | (value: T) => ReturnType<F>
        : never;

@somnicattus
Copy link
Contributor Author

somnicattus commented Mar 24, 2024

Hello.

I published remeda-compatible npm package rotery, which expands remeda with Iterators and AsyncIterators support.

https://github.com/somnicattus/rotery/tree/master/src/compositions

In this package I suggested type safe curry and purry.

Can you see how worth it is?

  • purry rejects optional parameters and rest parameters by type.
  • purry is type safe for overloading.
  • overloading is easy with Curried and Purried type.

@eranhirsch eranhirsch added feature request New feature or request typing Something type related labels Mar 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request New feature or request typing Something type related
Projects
None yet
Development

No branches or pull requests

2 participants