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

Typescript support #7

Open
aleclarson opened this issue Sep 20, 2018 · 5 comments
Open

Typescript support #7

aleclarson opened this issue Sep 20, 2018 · 5 comments

Comments

@aleclarson
Copy link

aleclarson commented Sep 20, 2018

I've been looking into adding Typescript support to this library. Here are the issues I encountered.

  1. JSX factory invocations (eg: <Foo />) return ReactElement<any> (but a fix is on the way), which means we can't infer the prop types of any returned elements

  2. @types/react does not allow the children prop to be a function (at least with "stateless components") This is only true if you explicitly type your stateless component using React.SFC

  3. Typescript cannot infer what yield returns, which means render props are untyped by default yield operator in generator functions has any return type microsoft/TypeScript#26959

Now I'm wondering if "yield to wrap the next yield or return" is worth the lack of type safety, or if I should bite the bullet on using render props without this library. 😞

If generators were always immutable in Javascript, this would be a lot easier.

@aleclarson
Copy link
Author

aleclarson commented Sep 20, 2018

The best workaround is something like:

import { Component, ReactNode } from 'react'

interface ParentProps<T extends object = any> {
  children?: ReactNode[] | ((props: T) => ReactNode)
}

// Infer the argument types of a function
type In<T> = T extends (...args: infer U) => any ? U : []

/** The functional "render children" prop */
type ChildFunction<T extends ParentProps> = Exclude<
  T['children'],
  ReactNode[] | undefined
>

// Component with render props
type RPC = Component<ParentProps> | ((props: ParentProps) => ReactNode)

/** Extract the render props of a component */
export type RenderProps<T extends RPC> = In<
  ChildFunction<T extends Component<ParentProps> ? T['props'] : In<T>[0]>
>[0]

And then:

import { Component, ReactNode } from 'react'
import epitath, { RenderProps } from 'epitath'

declare const Foo: (props: FooProps) => ReactNode
declare type FooProps = {
  children?: (value: { a: number }) => ReactNode
}

declare class Bar extends Component<BarProps> {}
declare type BarProps = {
  children?: (value: { b: number }) => ReactNode
}

const App = epitath(function* () {
  const { a }: RenderProps<typeof Foo> = yield <Foo />
  const { b }: RenderProps<Bar> = yield <Bar />
  return a + b
})

Example: https://codesandbox.io/s/qqonn05154?view=editor (note: CodeSandbox is using TS 2.7 so not exactly a working example right now)

@fakenickels
Copy link
Contributor

That is interesting! I don't know much about TS myself, but I'm going to read more about those and see if we can ship support right away

@aleclarson
Copy link
Author

Nice. Also, I opened microsoft/TypeScript#27267 to see if better support for immutable generators could be added.

@williamboman
Copy link
Contributor

Just to share my config which works so-and-so (having typing issues with yielded elements not having an explicit children prop)

declare module "epitath" {
    import * as React from "react"

    const epitath: <P extends {}>(
        Component: (props: P) => IterableIterator<React.ReactElement<any>>
    ) => React.ComponentType<P>

    export default epitath
}

// interface Props { foo: string }
// Usage: const FooComponent = epitath<Props>(function* ({ foo }) { ... })

@fakenickels
Copy link
Contributor

FYI meanwhile we discuss about this, ReasonML implementation of Epitath gave free type inference support
https://medium.com/astrocoders/render-props-composition-for-reasonml-is-here-b9c004ca9fcb

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

3 participants