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

addIndex #6

Open
Harris-Miller opened this issue Mar 30, 2023 · 1 comment
Open

addIndex #6

Harris-Miller opened this issue Mar 30, 2023 · 1 comment

Comments

@Harris-Miller
Copy link
Collaborator

Harris-Miller commented Mar 30, 2023

Draft branch: https://github.com/ramda/types/tree/addIndex

Current issues

addIndex is particularly unique in terms of type definitions because of how the return type depends on the given function

Let's use filter as an example

// Special case for filter
export function addIndex<T>(
  fn: (f: (item: T) => boolean, list: readonly T[]) => T[],
): _.F.Curry<(a: (item: T, idx: number, list: T[]) => boolean, b: readonly T[]) => T[]>;

To actually use it...

const filterIndexed = addIndex<number>(filter);

filterIndexed(predicate, [1, 2, 3, 4]); // ok
filterIndexed(predicate, ['a', 'b', 'c', 'd']); // error

The problem here is that you have to declare the generic for the type of the Array you intend to use it for. This defeats the purpose of generics in general, however, not passing the generic means that the type is unknown, which means the return type for all use cases is unknown[]

const filterIndexed = addIndex(filter);

filterIndexed(predicate, [1, 2, 3, 4]); // returns `unknown[]`
filterIndexed(predicate, ['a', 'b', 'c', 'd']); // returns `unknown[]`

That at least lets you use filterIndexed for any type, but then you lose all type inference. Not just for the the list, but for the predicate as well

addIndex<string>(filter)(isEven, ['a', 'b', 'c', 'd']); // error on isEven, because `(a: number) => boolean !== (a: string) => boolean`
addIndex(filter)(isEven, ['a', 'b', 'c', 'd']); // no error, because everything here is `unknown`

isEven also needs to be isEven = (item: number, i: number, list: number[]) => boolean otherwise it errors as well. This means that the inner fn type needs to support all overloads

Fix proposals

TODO

@TClark1011
Copy link

TClark1011 commented Oct 16, 2023

Played around with this a bit and ended up with this: https://tsplay.dev/mqq5jm

Its not perfect (eg; auto type inference confuses map for forEach), however it maintains the created function's generic signature, meaning you don't have to create a new instance of addIndex(map) for every different kind of array type you use it on.

Also its entirely possible this is already well known, but while working on this I thought of an approach to defining overloads that return generic functions with multiple curried call signatures:

declare function indexedReduceSignature<T, R>(
  callback: IndexedReduceCallback<T, R>,
  acc: R,
  list: T[],
): R;
declare function indexedReduceSignature<T, R>(
  callback: IndexedReduceCallback<T, R>,
  acc: R,
): (list: T[]) => R;

declare function indexedMapSignature<T, N>(
  callback: IndexedMapCallback<T, N>,
  list: T[],
): N[];
declare function indexedMapSignature<T, N>(
  callback: IndexedMapCallback<T, N>,
): (list: T[]) => N[];

/**
 * Reduce
 */
export function myAddIndex(
  fn: (
    callback: NonIndexedReduceCallback<any, any>,
    acc: any,
    list: any[],
  ) => any,
): typeof indexedReduceSignature;
/**
 * Map
 */
export function myAddIndex(
  fn: (callback: NonIndexedMapCallback<any, any>, list: any[]) => any[],
): typeof indexedMapSignature;

By defining the different possible output shapes as their own functions with their own overloads, and then referencing them with typeof , typescript will maintain the integrity of each signature. Perhaps this could be used for addressing some of the other problematic type signatures.

Edit: The approach I described is not actually as powerful as I though, I tried applying it to the take function types but it ends up with the same issues the current one already has: https://tsplay.dev/w2QaYW. You can see the f variable there has a return type of unknown[].

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

No branches or pull requests

2 participants