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

@types/react Make it possible to render a string from a functional component #20544

Closed
4 tasks done
janb87 opened this issue Oct 13, 2017 · 22 comments · Fixed by #65135
Closed
4 tasks done

@types/react Make it possible to render a string from a functional component #20544

janb87 opened this issue Oct 13, 2017 · 22 comments · Fixed by #65135

Comments

@janb87
Copy link
Contributor

janb87 commented Oct 13, 2017

With current typings it is not possible to create a stateless component which returns a string, number, boolean... (new in React 16)

import React from 'react'

const TestString = () => {
    return 'Test String Component'
}

const Component = () => (
    <div>
        <TestString/>
    </div>
)

Error I get:

JSX element type 'string' is not a constructor function for JSX elements.

The first problem is that currently in the typings a stateless component cannot return anything else besides an instance of a React.Element. This should be changed to (I think, I've based it upon the changes on the rendermethod, see https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L422)

interface StatelessComponent<P = {}> {
        (props: P & { children?: ReactNode }, context?: any): ReactElement<any> | Array<ReactElement<any>> | string | number | null;
        propTypes?: ValidationMap<P>;
        contextTypes?: ValidationMap<any>;
        defaultProps?: Partial<P>;
        displayName?: string;
}

Second problem is that behind the scenes the compiler will convert the jsx to `React.createElement('Test String Component', null)``

I get a compiler error that is saying that this is not a valid value. Seems that it has to be one of the values specified in following list: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L3465.

Any thoughts on how this can be fixed?

@apexskier
Copy link
Contributor

apexskier commented Oct 16, 2017

We could probably define stateless and class based components as returning ReactNode now. I haven't migrated to v16 yes, so that would need testing.

Looks like there's already a PR open. #20097

@levino
Copy link
Contributor

levino commented Oct 30, 2017

Is this being worked on atm?

@polco
Copy link
Contributor

polco commented Feb 5, 2018

I tried changing it but it breaks the JSX parser if the return type is string boolean or undefined.

@vbfox
Copy link
Contributor

vbfox commented Feb 5, 2018

There is a TypeScript PR in progress about it : microsoft/TypeScript#20239 (Well its about arrays but it's generally how TS should know what is allowed in a JSX component)

@polco
Copy link
Contributor

polco commented Feb 5, 2018

Cool, thanks for the update!

@jasonrhodes
Copy link

What's the latest on this? I see the TS PR mentioned above (microsoft/TypeScript#20239) is now closed/locked. I ran into the same thing today, new to typescript + react, and wondering why my components can't return strings suddenly...

@sonnyp
Copy link

sonnyp commented Oct 30, 2018

A workaround is to return a fragment.

return <>0</>

@zheeeng
Copy link

zheeeng commented Nov 3, 2018

Any news on this issue?

@eltonio450
Copy link

Hello, any update or hints on this please :) ?

@eps1lon
Copy link
Collaborator

eps1lon commented Feb 1, 2019

This is essentially a duplicate of #18912. Once microsoft/TypeScript#21699 is resolved we should be able to use ReactNode as a valid return type for function components which includes string and boolean.

@monfera
Copy link

monfera commented Apr 23, 2019

To generalize the issue, why should it even assume what's legit for React? With the @jsx / @jsxFrag Babel pragmas, an approach like this JSX can work with whatever functions. So it's not even that only strings should be admitted; any return value can be legit, constrained only by the specific use. React is important but it's just a specific rendering library.

@bryanrasmussen
Copy link

This has been open for nearly a couple years now, is there any solution?

About the suggested return a fragment <>StringValue goes here</> - I guess I am having trouble understanding how I get the string value, or any other type of value that is not a React.Element really to use in my code - for example if I have a function named Howdy returning <>"hello"</> and inside of componentDidMount I have

const whatSays =

how would I get the string value out of the fragment. I'm not seeing a way, it feels like any way to get around this is an extremely ugly hack and considering that the Hooks api is often used with returning just strings or other non React Element values - for example https://github.com/pankod/react-hooks-screen-type/blob/master/src/index.js
it seems to me that this bug really makes the Hooks api unusable, and as such makes Typescript a poor fit for React.

@eps1lon
Copy link
Collaborator

eps1lon commented Jul 24, 2019

This has been open for nearly a couple years now, is there any solution?

@bryanrasmussen See #20544 (comment) for the last update.

@leidegre
Copy link

leidegre commented Feb 27, 2020

This issue is making us write a-typical React code to work around limitations imposed by TypeScript. The only way to squash these errors in a TypeScript way is to introduce additional DOM elements and/or React.Fragments these add overhead (generate garbage) and they aren't needed.

This isn't always a problem by it crops up every now and is triggered when you return valid things from React components that aren't exactly JSX.Element, for example, an array.

I'd be happy with a sensible type annotation to help things along but I have to squash these with imperfect things like additional layers and/or any. Not good.

@ghost
Copy link

ghost commented Jun 4, 2020

Wrapping with an unnecessary React.Fragment has a (insignificant) cost at runtime.

Could this be another work around with no runtime penalty ?

import React, { ReactElement } from 'react'

const TestString = () => {
  return ('Test String Component' as unknown) as ReactElement
}

const Component = () => (
  <div>
    <TestString />
  </div>
)

export default Component

@leidegre
Copy link

leidegre commented Jun 4, 2020

Wrapping with an unnecessary React.Fragment has a (insignificant) cost at runtime.

It's a compromise. Still, would like to see better typing for this. Also, not insignificant, depends on context. A few fragments don't hurt, a lot of them will.

@levino
Copy link
Contributor

levino commented Jun 4, 2020

Wrapping with an unnecessary React.Fragment has a (insignificant) cost at runtime.

Could this be another work around with no runtime penalty ?

import React, { ReactElement } from 'react'

const TestString = () => {
  return ('Test String Component' as unknown) as ReactElement
}

const Component = () => (
  <div>
    <TestString />
  </div>
)

export default Component

Yeah, but then I also lose the type checking for the function. I mean I can also remove TS from my project without any runtime penalty but then I have no type checking. So your workaround is surely working but just breaks the purpose of TS. Casting to "what you expect" is like saying "Okay, then lets throw out the breaks. Who needs this anyhow?". When you wrap it in a fragment, you get correct type checking!

@vadimyen
Copy link

So, any news on fix?

tricoder42 added a commit to lingui/js-lingui that referenced this issue Oct 17, 2020
Fix Trans component type by wrapping string return value into
a fragment. Closes #700

Related DefinitelyTyped/DefinitelyTyped#20544
@orta
Copy link
Collaborator

orta commented Jun 7, 2021

Hi thread, we're moving DefinitelyTyped to use GitHub Discussions for conversations the @types modules in DefinitelyTyped.

To help with the transition, we're closing all issues which haven't had activity in the last 6 months, which includes this issue. If you think closing this issue is a mistake, please pop into the TypeScript Community Discord and mention the issue in the definitely-typed channel.

@orta orta closed this as completed Jun 7, 2021
@adamdicarlo
Copy link

This discussion thread is relevant: #53846

I don't see any other discussion threads for this topic.

(I'm hoping GitHub will cross-link the discussion back here like it does when mentioning PRs/issues. 🤞🏼)

@levino
Copy link
Contributor

levino commented Jun 29, 2021

(I'm hoping GitHub will cross-link the discussion back here like it does when mentioning PRs/issues. 🤞🏼)

Did not work.

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

Successfully merging a pull request may close this issue.