Skip to content

Conversation

@danielkcz
Copy link
Contributor

@danielkcz danielkcz commented Feb 5, 2018

Please fill in this template.

  • Use a meaningful title for the pull request. Include the name of the package modified.
  • Test the change in your own code. (Compile and run.)
  • Follow the advice from the readme.
  • Avoid common mistakes.
  • Run npm run lint package-name (or tsc if no tslint.json is present).

If changing an existing definition:

What is the current behavior?

https://codesandbox.io/s/r555ro878p

If you open that sandbox and wait for a compiler to catch up, you see that TextReturningComponent has an error. It is expecting ReactElement<any> | null to be returned.

const TextReturningComponent: React.SFC = () => 'sometext'

What is the expected behavior?

Based on the blog post it should be possible to have a component like that. Is that correct?

Looking at the type definition for render method it has return type ReactNode which is correct. However, the SFC declaration looks like this...

interface StatelessComponent<P = {}> {
  (props: P & { children?: ReactNode }, context?: any): ReactElement<any> | null;
}

I tried changing the return type to ReactNode. However, it generates bunch of errors. I am still pretty much rookie to TypeScript, so I am not sure how to fix these. Can someone help me with that?

@typescript-bot typescript-bot added Popular package This PR affects a popular package (as counted by NPM download counts). Awaiting reviewer feedback labels Feb 5, 2018
@typescript-bot
Copy link
Contributor

typescript-bot commented Feb 5, 2018

@FredyC Thank you for submitting this PR!

🔔 @johnnyreilly @bbenezech @pzavolinsky @digiguru @ericanderson @morcerf @tkrotoff @DovydasNavickas @onigoetz @theruther4d @guilhermehubner @JoshuaKGoldberg @jrakotoharisoa - please review this PR in the next few days. Be sure to explicitly select Approve or Request Changes in the GitHub UI so I know what's going on.

If no reviewer appears after a week, a DefinitelyTyped maintainer will review the PR instead.

@typescript-bot
Copy link
Contributor

@FredyC The Travis CI build failed! Please review the logs for more information. Once you've pushed the fixes, the build will automatically re-run. Thanks!

type SFC<P = {}> = StatelessComponent<P>;
interface StatelessComponent<P = {}> {
(props: P & { children?: ReactNode }, context?: any): ReactElement<any> | null;
(props: P & { children?: ReactNode }, context?: any): ReactNode;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is slightly wrong because it can't return undefined. ReactNode definition includes undefined.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, in that case, it's wrong altogether, because ReactNode is also used as a return type of render method for a class component.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough.

@johnnyreilly
Copy link
Member

Could you fix the issues with the CI build please?

@danielkcz
Copy link
Contributor Author

@johnnyreilly Well, as I said in OP...

I tried changing the return type to ReactNode. However, it generates bunch of errors. I am still pretty much rookie to TypeScript, so I am not sure how to fix these. Can someone help me with that?

I am kinda waiting for someone more capable of figuring this out :/

@ericanderson
Copy link
Contributor

I took a quick look, given they all fail on typescript@next, you may be hitting an issue with typescript itself?

@polco
Copy link
Contributor

polco commented Feb 6, 2018

here is the Typescript PR about that : microsoft/TypeScript#20239
and another issue related: #20544

@danielkcz
Copy link
Contributor Author

@polco Ah, I've missed that issue, thanks. So I guess we have to wait for this.

I am curious though, how is it possible that having render method with ReactNode type is working just fine and for an SFC it is not. What's is the difference? Both are functions in the end.

@polco
Copy link
Contributor

polco commented Feb 6, 2018

Because the Typescript JSX parser has some custom React logic.

@weswigham
Copy link
Contributor

Out of curiosity, if you change this line to export type Element = ReactNode, do you get the behavior you want? I think you aught to, without changing TS. The downside is that it could be breaky for people who wrote something like class Component implements JSX.Element {}, expecting JSX.Element to be an alias for React.ReactElement<any>; but at least the fix is simple.

Remove undefined from ReactNode
@weswigham
Copy link
Contributor

weswigham commented Feb 6, 2018

It look like the many breaks in travis are all packages who depend in Element being an object type that's an alias for ReactElement. ☹️ I fear that for compat reasons, react may be backed into a little bit of a corner here. It'll be a uuuuuuuuuuuuuuuuuuuuuge break to change it to an alias, it looks like.

@johnnyreilly @mhegazy we (likely) don't need to change TS to fix the original problem, (as microsoft/TypeScript#20239 made it look like we may have), but this is probably the biggest break I've ever seen in the react .d.ts, as it fundamentally changes the most core type in the library. What should we do? react actually does operate on this type now, but we've got so many people using JSX.Element as an alias for React.ReactElement<any>, that this is total-ecosystem-wide breakage.

@weswigham
Copy link
Contributor

Ah, there is also an issue in TS. We couple the return type of the factory function with the allowable return types of a SFC or class render method. In reality, those two are much more permissive than what createElement can possibly return. We really need to get these types from the factory function signature and not the JSX namespace now...

@danielkcz
Copy link
Contributor Author

So do we have any ideas how to solve this? I am still pretty much confused about some things here and I am not sure what should be the next step. Perhaps fix the mentioned problem in TS first? Should I create an issue for that?

@ericanderson
Copy link
Contributor

Yesterday I tried to change the types to make it possible to restrict children to certain components. (eg can only have s). The idea was to capture the type into JSX.Element so that children could be more restrictive.

I also hit this problem in my efforts.

I bring this up so that if we make a breaking change to TS, perhaps we could be sure to enable this as possible.

@danielkcz
Copy link
Contributor Author

danielkcz commented Apr 9, 2018

@weswigham I am curious. Will this PR be still necessary after your work in microsoft/TypeScript#21699 is finished? It's rather overwhelming and I am not sure what to take from that.

const StatelessComponent4: React.SFC = props => null;

// allows string as return value
const StatelessComponent5: React.SFC = props => "string";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be useful to also include an $ExpectError for the undefined return.

@typescript-bot typescript-bot added Has Merge Conflict This PR can't be merged because it has a merge conflict. The author needs to update it. Abandoned This PR had no activity for a long time, and is considered abandoned Merge:Express Owner Approved A listed owner of this package signed off on the pull request. and removed Has Merge Conflict This PR can't be merged because it has a merge conflict. The author needs to update it. Merge:Express Abandoned This PR had no activity for a long time, and is considered abandoned labels Apr 17, 2018
@typescript-bot
Copy link
Contributor

@FredyC To keep things tidy, we have to close PRs that aren't mergeable but don't have activity from their author. No worries, though - please open a new PR if you'd like to continue with this change. Thank you!

@ScreamZ
Copy link
Contributor

ScreamZ commented Jan 17, 2019

@gaearon @JoshuaKGoldberg @FredyC Currently this make tsc yield

const A: React.FunctionComponent<{}> = props => props.children

I had to do the following

const A: React.FunctionComponent<{}> = props => props.children as React.ReactElement<any>

Any idea ?

@apieceofbart
Copy link
Contributor

@weswigham Does it mean it will never be fixed beacuse it's a breaking change? Can we do anything about informing community to have this fixed?
I think it's extremely confusing for people starting with Typescript in React that both of these fail:

const IJustPassChildren: FC = ({ children }) => {
  return children;
};

const IJustPassChildren = ({ children }: { children: ReactNode }) => {
  return children;
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Abandoned This PR had no activity for a long time, and is considered abandoned Has Merge Conflict This PR can't be merged because it has a merge conflict. The author needs to update it. Owner Approved A listed owner of this package signed off on the pull request. Popular package This PR affects a popular package (as counted by NPM download counts).

Projects

None yet

Development

Successfully merging this pull request may close these issues.