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

Cannot type flatMap polyfill #31015

Closed
texastoland opened this issue Apr 18, 2019 · 5 comments
Closed

Cannot type flatMap polyfill #31015

texastoland opened this issue Apr 18, 2019 · 5 comments
Labels
Needs More Info The issue still hasn't been fully clarified

Comments

@texastoland
Copy link
Contributor

texastoland commented Apr 18, 2019

TypeScript Version: 3.4.3

Search Terms:

Code

Array.prototype.flatMap = function(f, that?) {
  // infer U
  type ElementType<T> = T extends readonly any[] ? T[number] : T
  type U = ElementType<ReturnType<typeof f>>
  // polyfill
  const ys: U[] = []
  this.forEach((x, i, xs) => {
    const y = f.call(that!, x, i, xs)
    Array.isArray(y) ? ys.push(...(y as U[])) : ys.push(y as U)
  })
  return ys
}

Expected behavior:

This an umbrella issue. I ran into multiple issues trying to polyfill Array#flatMap without duplicating the type signature from "lib": ["esnext"]:

interface Array<T> {
    flatMap<U, This = undefined> (
        callback: (this: This, value: T, index: number, array: T[]) => U|ReadonlyArray<U>,
        thisArg?: This
    ): U[]
}

Actual behavior:

  1. ReturnType<typeof Array.prototype.flatMap> types as {}[].
  2. ElementType<ReturnType<typeof f>> types as U | ElementType<U> instead of unifying on U.
  3. f.call(that, x, i, xs) errors with Argument of type 'This | undefined' is not assignable to parameter of type 'This'..
  4. Type guards don't appear to work in Array.isArray(y) ? ys.push(...y) : ys.push(y). The inferred types are (U | readonly U[]) ? (any[]) : (U | readonly U[]). I suspect it has to do with readonly.

Playground Link:

Not reproducible on the Playground, because it requires "lib": ["esnext"]. I could share a CodeSandbox, though.

Related Issues:

@RyanCavanaugh RyanCavanaugh added the Needs More Info The issue still hasn't been fully clarified label Apr 18, 2019
@RyanCavanaugh
Copy link
Member

What is the bug here?

@texastoland
Copy link
Contributor Author

texastoland commented Apr 18, 2019

The 4 numbered under Actual behavior I would all qualify as bugs. I'm trying to track down related tickets (unsuccessful last night), but it's the first time ever (to its credit) using TypeScript that I've encountered several at once. I reported it as an umbrella issue, because it's not at all contrived code. I'm just trying to implement a type in the standard lib. Even if I break it down to individual tickets, I'd prefer to link back to my use case. I ran into an additional issue trying to implement this with JSDoc syntax, but I did find tickets related to that.

@RyanCavanaugh
Copy link
Member

RyanCavanaugh commented Apr 18, 2019

1: I don't see this in 3.4
2: This is the correct behavior; the resolution needs to be deferred until instantiation
3: That's a correct error; that originates in an optional parameter and is potentially undefined
4: #17002

@texastoland
Copy link
Contributor Author

texastoland commented Apr 18, 2019

Thank you!

  1. I'm on 3.4.3:
    type MapRet = ReturnType<typeof Array.prototype.map>
    // => type MapRet = {}[]
    // type MapRet<T> = ReturnType<typeof Array.prototype.map<T>>
    // => error: '>' expected.
    This looks like it works as designed, but I have no idea how to get what I expect.
  2. 👍
  3. Does that mean the flatMap<U, This = undefined> signature is unimplementable? I couldn't find any documentation on <This = undefined> and I couldn't make call(that, ...) work with an explicit signature, either.
  4. 🙏

@texastoland
Copy link
Contributor Author

1. ReturnType<typeof Array.prototype.flatMap> types as {}[].

#28655 et al. with workaround.

3. I couldn't find any documentation on <This = undefined>

Release notes.

I couldn't make call(that, ...) work with an explicit signature, either.

call<?This, Blabbity, Bloop>(that, ...).

3. Does that mean the flatMap<U, This = undefined> signature is unimplementable?

Not without dancing around null checks, no.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs More Info The issue still hasn't been fully clarified
Projects
None yet
Development

No branches or pull requests

2 participants