Skip to content

JSX error reporting is inconsistent #65316

Open

Description

Acknowledgement

  • I acknowledge that issues using this template may be closed without further explanation at the maintainer's discretion.

Comment

While implementing the MDX content mapper I paid a lot of attention to how TypeScript reports JSX related errors. I’ll gladly provide more context if needed, but the TL;DR is that it maps to JSX, and users can inject custom JSX components using a _components object.

I noticed that JSX related type errors are inconsistent, excessive, and sometimes even have incorrect messages. The errors all point the user into a direction to solve the error, but I believe DX can be improved.

Example

Let’s say we have the following files:

// package.json
{
  "type": "module",
  "dependencies": {
    "@types/react": "19.3.0",
    "typescript": "7.1.0-beta"
  }
}
// tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "module": "preserve",
    "noEmit": true
  }
}
// code.jsx
export function Component() {
  const _components = {
    div: 'div',
    dov: 'dov'
  } as const

  return (
    <>
      <div></div>
      <dov></dov>
      <dav></dav>
      <div></dov>
      <div></dav>
      <_components.div></_components.div>
      <_components.dov></_components.dov>
      <_components.dav></_components.dav>
      <_components.div></_components.dov>
      <_components.div></_components.dav>
    </>
  )
}

In my editor, I see 15 errors.

Since this file has syntax errors, tsc only reports syntactic errors:

$ tsc
code.tsx:12:14 - error TS17002: Expected corresponding JSX closing tag for 'div'.

12       <div></dov>
                ~~~

code.tsx:13:14 - error TS17002: Expected corresponding JSX closing tag for 'div'.

13       <div></dav>
                ~~~

code.tsx:17:26 - error TS17002: Expected corresponding JSX closing tag for '_components.div'.

17       <_components.div></_components.dov>
                            ~~~~~~~~~~~~~~~

code.tsx:18:26 - error TS17002: Expected corresponding JSX closing tag for '_components.div'.

18       <_components.div></_components.dav>
                            ~~~~~~~~~~~~~~~


Found 4 errors in the same file, starting at: code.tsx:12
  • All error messages make sense
  • The error messages underline the full JSX closing tag name (_components.dov)

Let’s fix these errors:

// code.tsx
export function Component() {
  const _components = {
    div: 'div',
    dov: 'dov'
  } as const

  return (
    <>
      <div></div>
      <dov></dov>
      <dav></dav>
      <_components.div></_components.div>
      <_components.dov></_components.dov>
      <_components.dav></_components.dav>
    </>
  )
}
$ tsc
code.tsx:10:7 - error TS2339: Property 'dov' does not exist on type 'JSX.IntrinsicElements'.

10       <dov></dov>
         ~~~~~

code.tsx:10:12 - error TS2339: Property 'dov' does not exist on type 'JSX.IntrinsicElements'.

10       <dov></dov>
              ~~~~~~

code.tsx:11:7 - error TS2339: Property 'dav' does not exist on type 'JSX.IntrinsicElements'.

11       <dav></dav>
         ~~~~~

code.tsx:11:12 - error TS2339: Property 'dav' does not exist on type 'JSX.IntrinsicElements'.

11       <dav></dav>
              ~~~~~~

code.tsx:13:7 - error TS2339: Property 'dov' does not exist on type 'JSX.IntrinsicElements'.

13       <_components.dov></_components.dov>
         ~~~~~~~~~~~~~~~~~

code.tsx:13:8 - error TS2604: JSX element type '_components.dov' does not have any construct or call signatures.

13       <_components.dov></_components.dov>
          ~~~~~~~~~~~~~~~

code.tsx:14:20 - error TS2339: Property 'dav' does not exist on type '{ readonly div: "div"; readonly dov: "dov"; }'.

14       <_components.dav></_components.dav>
                      ~~~

code.tsx:14:38 - error TS2339: Property 'dav' does not exist on type '{ readonly div: "div"; readonly dov: "dov"; }'.

14       <_components.dav></_components.dav>
                                        ~~~


Found 8 errors in the same file, starting at: code.tsx:10

Let’s break down these errors:

code.tsx:10:7 - error TS2339: Property 'dov' does not exist on type 'JSX.IntrinsicElements'.

10       <dov></dov>
         ~~~~~

code.tsx:11:7 - error TS2339: Property 'dav' does not exist on type 'JSX.IntrinsicElements'.

11       <dav></dav>
         ~~~~~

This makes sense. Note that the scope is the entire JSX open tag (<dov>)

code.tsx:10:12 - error TS2339: Property 'dov' does not exist on type 'JSX.IntrinsicElements'.

10       <dov></dov>
              ~~~~~~

code.tsx:11:12 - error TS2339: Property 'dav' does not exist on type 'JSX.IntrinsicElements'.

11       <dav></dav>
              ~~~~~~

This is redundant repetition. The same error was reported for the open tags. Note that the scope is the entire JSX closing tag (</dov>)

code.tsx:13:7 - error TS2339: Property 'dov' does not exist on type 'JSX.IntrinsicElements'.

13       <_components.dov></_components.dov>
         ~~~~~~~~~~~~~~~~~

This makes sense. Note that the scope is the entire JSX open tag (<_components.dov>)

code.tsx:13:8 - error TS2604: JSX element type '_components.dov' does not have any construct or call signatures.

13       <_components.dov></_components.dov>
          ~~~~~~~~~~~~~~~

This is confusing. Indeed, _components.dov has no construct or call signatures, but neither does _components.div. This also seems to be the same issue as above. _components.dov is not a valid JSX signature.

I imagine this has to with ElementType vs IntrinsicElements, but as an end user, this really looks like a duplicate error. And the message is just not helpful.

Note that the scope is the JSX tag name (_components.dov)

code.tsx:14:20 - error TS2339: Property 'dav' does not exist on type '{ readonly div: "div"; readonly dov: "dov"; }'.

14       <_components.dav></_components.dav>
                      ~~~

This makes sense. Note that the scope is the dav member expression.

code.tsx:14:38 - error TS2339: Property 'dav' does not exist on type '{ readonly div: "div"; readonly dov: "dov"; }'.

14       <_components.dav></_components.dav>
                                        ~~~

This is redundant repetition. The same error was reported for the open tags. Note that the scope is the dav member expression.

No errors are reported for the </_components.dov> closing tags

So far, we have 4 + 8 = 12 errors from the CI output. However, the editor originally reported 15. The remaining 3 errors are related to incorrectness of the closing tags that were originally mismatched. They can only appear as semantic errors if syntactic errors are produced.

Suggestions

I have a couple of suggestions to improve this situation:

  1. If a JSX open tag has an error, don’t repeat it for the closing tag.
  2. Never report TS2339 and TS2604. Make them mutually exclusive.
  3. Improve the error message for TS2604 to clarify some strings are valid.
  4. Narrow the scope of all JSX tag errors to the JSX name (dov), not the entire element (<dov>). The scope of the entire element can become huge if props are involved.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions