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

flat/flatMap missing - but how would I type a flat() function anyway? #6602

Closed
lll000111 opened this issue Jul 16, 2018 · 11 comments
Closed

Comments

@lll000111
Copy link
Contributor

lll000111 commented Jul 16, 2018

Two parts to this question, because I came across the native JS function while writing my own (it's a common function in any case).

Stage 3 proposal:

Typescript uses this monster definition - each possible level gets its own definition: https://github.com/Microsoft/TypeScript/blob/master/src/lib/esnext.array.d.ts

I came across the problem of having to type a flat() function after I added one to my own project:

const flat = function <T> (arr: Array<T | Array<T>>, depth?: number = 1): Array<T> {
   ...
};

This does not work. I cannot use just T, nor Array<T> (since at the very least I want to allow pure values as array items as well as arrays of pure values, for a nesting level of 1), nor does T | Array<T> seem to work even though I only have depth:1 arrays in my code.

Looking at the Microsoft definition of flat, this looks like a nightmare function to add types to. How would I do this in my code for my own function anyway? In the global definition which one could use something similar to what Typescript has, but when I document my own code and my own function inline I can only add one single type.

So another way to phrase this, when I don't write library definition types but type directly in the respective function, how can I have multiple different interfaces? interface in Flow seems to be for classes, I probably need different interfaces for the same function though (I don't have any classes in my functional-style code anyway).

I looked for an example how it was solved for libraries like Ramda which provide a flatten function, found this, and they use

declare function flatten<T>(x: T[]): T[];
declare function flatten(x: any[]): any[];

but the any route is not where I think this should end.

Related(?): #4305, #60


I also tried intersections as shown here: https://stackoverflow.com/a/45843913/544779

My intersection looks like this.

type FlattenFN =
    & (<T> (arr: T[], depth: 0, currentDepth?: number, result?:T[]) => T[])
    & (<T> (arr: Array<T | T[]>, depth: 1, currentDepth?: number, result?: T[]) => T[]);

but then I'm not sure about the generics parameter: Write type FlattenFN? But where do I get the type from when I actually use the intersection type:

const flatten: FlattenFN<???> = function (arr, depth, currentDepth = 0, result = []) {...}

Flow Try Link

@lll000111
Copy link
Contributor Author

lll000111 commented Jul 16, 2018

I managed to get the easy cases to work using

type NestedArray<T> = Array<T | NestedArray<T>>;
function flatten<T>(xs: NestedArray<T>): Array<T> {
  ....
}

taken from here.

However, each time there is a promise somewhere it all fails. I'll have to investigate, I've seen that in many other experiments, always the Flow built-in promise definition caused problems.

@lll000111
Copy link
Contributor Author

I ended up using the definition from flow-typed for iodash.

@Krumpet
Copy link

Krumpet commented Nov 11, 2018

I managed to get the easy cases to work using

type NestedArray<T> = Array<T | NestedArray<T>>;
function flatten<T>(xs: NestedArray<T>): Array<T> {
  ....
}

taken from here.

However, each time there is a promise somewhere it all fails. I'll have to investigate, I've seen that in many other experiments, always the Flow built-in promise definition caused problems.

How did this type work when it circularly references itself? I'm getting Type alias 'NestedArray circularly references itself' in TS playground.

@lll000111
Copy link
Contributor Author

in TS playground

TS? How about Flow Playground?

@Krumpet
Copy link

Krumpet commented Nov 11, 2018

I saw that you mentioned Typescript in the opening post and thought this code is valid TS, my bad!

@conradreuter
Copy link

My two cents: for properly typing Array.prototype.flat there might be no other way than having one general case and then typing out all the special cases up to a certain depth.

For instance, flattening Array<Array<Array<string>>> with a depth of 2 should yield Array<string>, and I don't know a way of doing a generalized solution without having some advanced type-level programming.

(Feel free to correct me)

@gajus
Copy link

gajus commented Dec 15, 2018

Is there no way to extend the core $ReadOnlyArray definition?

declare class $ReadOnlyArray<+T> {

@bsmith-cycorp
Copy link

bsmith-cycorp commented Jan 22, 2019

Personally I'm okay with flat()'s return value bailing out to any, if that's the only way to get Flow to stop complaining about it not existing

@gajus
Copy link

gajus commented Jan 26, 2019

Related: #7397

@lll000111
Copy link
Contributor Author

lll000111 commented Jan 26, 2019

Reopened for @gajus and #7397 :-)

(Because there's quite a bit of useful text here already)

@lll000111
Copy link
Contributor Author

Closing this again in favor of #7397, one issue is enough. I had originally confused this with another issue that required a lot more context, but I copied the single important line from this issue right here to the other one so no need to keep this open.

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

5 participants