Definitelytyped: @types/react Make it possible to render a string from a functional component

Created on 13 Oct 2017  ·  19Comments  ·  Source: DefinitelyTyped/DefinitelyTyped

  • [x] I tried using the @types/react package and had problems.
  • [x] I tried using the latest stable version of tsc. https://www.npmjs.com/package/typescript
  • [x] I have a question that is inappropriate for StackOverflow. (Please ask any appropriate questions there).
  • [x] [Mention](https://github.com/blog/821-mention-somebody-they-re-notified) the authors (see Definitions by: in index.d.ts) so they can respond.

    • Authors: @johnnyreilly, @bbenezech, ...

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?

Most helpful comment

Any news on this issue?

All 19 comments

@DovydasNavickas @sandersn @tkrotoff @andy-ms @ddwwcruz @apexskier @devrelm @RyanCavanaugh @richseviora @onigoetz @minestarks @yuit @sboehler @plantain-00 @p-jackson @miracle2k @voxmatt @morcerf @janechu @gaspard @vbfox @ericanderson @dyst5422 @rapilabs @cynecx @newyankeecodeshop

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. https://github.com/DefinitelyTyped/DefinitelyTyped/pull/20097

Is this being worked on atm?

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

There is a TypeScript PR in progress about it : https://github.com/Microsoft/TypeScript/pull/20239 (Well its about arrays but it's generally how TS should know what is allowed in a JSX component)

Cool, thanks for the update!

What's the latest on this? I see the TS PR mentioned above (https://github.com/Microsoft/TypeScript/pull/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...

A workaround is to return a fragment.

return <>0</>

Any news on this issue?

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

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.

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.

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.

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

@bryanrasmussen See https://github.com/DefinitelyTyped/DefinitelyTyped/issues/20544#issuecomment-459665668 for the last update.

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.

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

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.

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!

So, any news on fix?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

demisx picture demisx  ·  3Comments

lilling picture lilling  ·  3Comments

JWT
svipas picture svipas  ·  3Comments

Loghorn picture Loghorn  ·  3Comments

jgoz picture jgoz  ·  3Comments