Skip to content

Broken jsx element with not static type with ts 3.2.1 #28768

Description

TypeScript Version: 3.2.1

Search Terms:
jsx

Code

import * as React from 'react';

function render(url?: string): React.ReactNode {
    const Tag = url? 'a' : 'button';
    return <Tag>test</Tag>;
}

Expected behavior:
Everything is ok. TypeScript 3.1.6 compiles it properly.

Actual behavior:
error: JSX element type 'Tag' does not have any construct or call signatures. [2604]

Activity

  1. devrelm commented on Nov 30, 2018

    @devrelm

    Related: #28631

  2. weswigham commented on Nov 30, 2018

    @weswigham
    Member

    It's because we started strongly checking tag calls such as these, and the 'a' tag's properties and 'button' tag's properties are heavily dissimilar (one isn't a strict subset of the other) - they're all optional, so a call like this can still succeed at runtime, but in the typesystem at present the two signatures are irreconcilable. Needs a full fix of #7294 to work well.

  3. rhys-vdw commented on Dec 2, 2018

    @rhys-vdw
    Contributor

    Please consider this now broken example:

    import React, {
      ReactType,
      StatelessComponent,
    } from "react"
    import classNames from "classnames"
    
    interface Props {
      className?: string
    }
    
    export function createStyledComponent<P extends { className: string }>(
      Component: ReactType<P>,
      className: string
    ): StatelessComponent<Props> {
      return props => (
        <Component className={classNames(className, props.className)}>
          {props.children}
        </Component>
      )
    }
    ERROR in [at-loader] ./app/assets/javascripts/utilities/component-factory.tsx:19:6
        TS2604: JSX element type 'Component' does not have any construct or call signatures.
    

    Is this something that should be addressed in @types/react?

  4. Jessidhia commented on Dec 3, 2018

    @Jessidhia

    Nothing that can be done on the @types/react side, or rather, what can be done is already done -- ReactType<P> will work more-or-less correctly with React.createElement.

    image

    Note that your P extends { className: string } will actually reject all DOM elements because their className includes undefined which is not assignable to string.

    image

  5. rhys-vdw commented on Dec 3, 2018

    @rhys-vdw
    Contributor

    @Kovensky Oops, the existing (working) code I had was:

    export function createStyledComponent(
      Component: ReactType,
      className: string
    ): StatelessComponent<Props> {
      return props => (
        <Component className={classNames(className, props.className)}>
          {props.children}
        </Component>
      )
    }

    That generic change was an attempt to get it working under TS 3.2.1

  6. azizhk commented on Dec 6, 2018

    @azizhk
    Contributor

    Just a small reproduction:
    https://repl.it/repls/InconsequentialBreakableCategories
    Go to package.json and change typescript version to 3.2.1 to see how this breaks.

    How I had to fix it:
    https://github.com/ritz078/pebble/pull/151/files#diff-288d98c45cab569122e391278589e7dbR123
    But still, that does not work as something fails in the union of types. Check how InputProps union type is defined. Now I can't do something like:

    const inp = (
      <Input
        textArea={abc as boolean}
      />
    )

    even though my union type is

    type InputProps = {
      textArea?: false
      // more types where textArea would be undefined or false
    } & {
      textArea: true
      // more types where textArea would be true
    }
  7. RyanCavanaugh commented on Mar 7, 2019

    @RyanCavanaugh
    Member

    Wesley Wigham (@weswigham) can we get a fresh read on this?

  8. weswigham commented on Mar 7, 2019

    @weswigham
    Member

    The OP's example should have been fixed by #29011 (and testing shows it is). Rhys van der Waerden (@rhys-vdw)'s example here also works. Aziz Khambati (@azizhk)'s example probably typechecks better than he wants, because we correctly reject the spread of a React.InputHTMLAttributes<HTMLTextAreaElement> as A's arguments, because a "input" element won't be happy getting a "textarea"'s onChange handler (or vice-versa).

  9. added
    BugA bug in TypeScript
    FixedA PR has been merged for this issue
    and removed
    Needs InvestigationThis issue needs a team member to investigate its status.
    on Mar 7, 2019
  10. azizhk commented on Mar 11, 2019

    @azizhk
    Contributor

    Hi, I just upgraded to

    "@types/react": "^16.8.7",
    "@types/react-dom": "^16.8.2",
    "typescript": "^3.3.3333",
    

    I can use this for string components now const A = condition ? "input" : "textarea",

    But I cannot use this for components which have generics

    class OptionGroupCheckBox<OptionType> extends React.PureComponent<OptionGroupProps<OptionType>> {
      // methods
    }
    
    class OptionGroupRadio<OptionType> extends React.PureComponent<OptionGroupProps<OptionType>> {
      // methods
    }
    
    const OptionGroup = condition ? OptionGroupCheckBox : OptionGroupRadio;
    const vdom = (
      <OptionGroup
        // OptionGroupProps<OptionType>
      />
    )
  11. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BugA bug in TypeScriptDomain: JSX/TSXRelates to the JSX parser and emitterFixedA PR has been merged for this issue

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions