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

Fix the JSX runtime types in RunOptions #2465

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

remcohaszing
Copy link
Member

Initial checklist

  • I read the support docs
  • I read the contributing guide
  • I agree to follow the code of conduct
  • I searched issues and couldn’t find anything (or linked relevant results below)
  • If applicable, I’ve added docs and tests

Description of changes

@types/react now has types for react/jsx-runtime and react/jsx-dev-runtime. These types are not compatible with the types provided by hast-util-to-jsx-runtime, which are used by MDX.

To resolve the issue, all runtime related options have been changed to unknown. Since the user is supposed to pass in whatever JSX runtime they imported, this should be sufficient. This removes the dependency on hast-util-to-jsx-runtime.

This fixes several type errors. An outdated workaround has been removed from the documentation.

Closes #2463

@remcohaszing remcohaszing added 🐛 type/bug This is a problem 📦 area/deps This affects dependencies ☂️ area/types This affects typings 👶 semver/patch This is a backwards-compatible fix 🤞 phase/open Post is being triaged manually labels Apr 5, 2024
Copy link

vercel bot commented Apr 5, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
mdx ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 2, 2024 0:16am

* Function to generate an element in development mode.
* @property {Jsx | null | undefined} [jsxs]
Copy link
Member

Choose a reason for hiding this comment

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

Should we at least use something like Function?
We could also improve the hast-util-to-jsx-runtime types?
Fixing the type error is nice, but removing all type errors around this, not so sure?

Copy link
Member Author

Choose a reason for hiding this comment

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

We could use Function, or better: (type: any, props: any) => any.

We should improve hast-util-to-jsx-runtime, but personally I think this type isn’t worth having an extra dependency regardless.

Copy link
Member

Choose a reason for hiding this comment

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

or better: (type: any, props: any) => any.

Right, and even better would be an even more correct type. We do know a lot about props and key. And at that point, it’s nice to have the more complex type defined and tested in a single place?

`@types/react` now has types for `react/jsx-runtime` and
`react/jsx-dev-runtime`. These types are not compatible with the types
provided by `hast-util-to-jsx-runtime`, which are used by MDX.

To resolve the issue, all runtime related options have been changed to
`unknown`. Since the user is supposed to pass in whatever JSX runtime
they imported, this should be sufficient.

This fixes several type errors. An outdated workaround has been removed
from the documentation.

Closes #2463
Copy link
Member

@wooorm wooorm left a comment

Choose a reason for hiding this comment

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

I guess the breaking change happened in the other PR, but Fragment and similar were types that were exposed from MDX. And now aren‘t.

@remcohaszing
Copy link
Member Author

The other PR only changed that they’re no longer exported from internal files. It could be considered breaking here. There’s not really a use for them anymore, but they can be defined as unknown to avoid breaking existing imports.

On the other hand, as a user I wouldn’t find it too bad if TypeScript forces me to make this change:

- /**
-  * @import {Fragment, Jsx} from '@mdx-js/mdx'
-  */
-
- import * as runtime_ from 'react/jxs-runtime'
-
- const runtime = /** @type {{Fragment: Fragment, jsx: Jsx, jsxs: Jsx}} */ (
-   /** @type {unknown} */ (runtime_)
- )
+ import * as runtime from 'react/jxs-runtime'

@wooorm
Copy link
Member

wooorm commented Jul 2, 2024

I think we’ve had this conversation partially a couple times already, the gist of my stance is: generally, we have types for things. so why are we now removing types?
I don’t consider hiding type errors by treating everything as unknown an improvement.
These things are at least functions. And probably stricter than that. Not unknown/any?

@remcohaszing
Copy link
Member Author

I think we should aim at the following:

  1. ✔️ Working runtime code.
  2. ❌ Types that make user code at least compile with TypeScript.
  3. ❌ Types that provide more helpful type checks.

While (3) is great, I strongly believe we need to aim for (2) first. That’s what this PR solves. I don’t think any users are helped with types that prevent the user from using the code without type casting (to / from unknown even) or @ts-ignore checks.

I doubt it’s useful, because users are typically supposed to pass in a known runtime, not define one. However, it is possible to use the following somewhat stricted types:

/**
 * @typedef {unknown} Fragment
 */

/**
 * @callback Jsx
 * @param {any} type
 * @param {object} props
 * @param {any} [key]
 */

/**
 * @callback JsxDev
 * @param {any} type
 * @param {object} props
 * @param {any} key
 * @param {boolean} isStatic
 * @param {object} [source]
 * @param {object} [self]
 */
  • Fragment: was already unknown.
  • type: must be any. The real type of type is defined by the JSX runtime. It can’t be unknown, because the type of type must be assignable to what the JSX runtime expects.
  • props: must be an object.
  • key: same thing as type
  • isStatic: of this we actually know it must be a boolean.
  • source: we know the shape, it might be a bit dangerous to assume the type provided by the JSX runtime is compatible.
  • self: is always an object.

The problem with those types is that type-coverage complains about some of the arguments being any. So CI fails.

@wooorm
Copy link
Member

wooorm commented Jul 2, 2024

I strongly believe we need to aim for (2) first

Why? I don‘t think we’ve done that before: implement an any instead of actually solving types. Why do you want to implement a simple workaround instead of solving this bug?

I doubt it’s useful, because users are typically supposed to pass in a known runtime, not define one. However, it is possible to use the following somewhat stricted types:

I do think that people should be able to type check jsx/jsxs/Fragment.

Also keep in mind that these types are from another project where this is important.
It’s important in several of our projects.
We can solve it once, in a good way?

The problem with those types is that type-coverage complains about some of the arguments being any. So CI fails.

That sounds like something that can be solved?

@remcohaszing
Copy link
Member Author

I strongly believe we need to aim for (2) first

Why? I don‘t think we’ve done that before: implement an any instead of actually solving types. Why do you want to implement a simple workaround instead of solving this bug?

TypeScript is supposed to help users. Types that are wrong are significantly worse than types that are loose. What you call a simple workaround, I call an imperfect, but pragmatic solution.

We actually do use any in a couple of places in this repo. It happens in places where we act as the user. This PR removes those occurrences.

I doubt it’s useful, because users are typically supposed to pass in a known runtime, not define one. However, it is possible to use the following somewhat stricted types:

I do think that people should be able to type check jsx/jsxs/Fragment.

Sure, it’s nice, but the user is bound to the types of their JSX framework and MDX. They just import one thing and pass it to a function. They don’t have a say in how any of it is typed or implemented. I’m not saying stricter types are bad, I’m saying the status quo (incorrect types) is significantly worse than loose types.

Also keep in mind that these types are from another project where this is important. It’s important in several of our projects. We can solve it once, in a good way?

hast-util-to-jsx-runtime is only used in MDX for its (incorrect) types. Yes, the problem needs to be solved there as well, but there’s no reason to block a fix in MDX for that.

The problem with those types is that type-coverage complains about some of the arguments being any. So CI fails.

That sounds like something that can be solved?

I don’t see how, except for disabling type-coverage, at least for the file that contains the any.

@wooorm
Copy link
Member

wooorm commented Jul 3, 2024

They just import one thing and pass it to a function.

I disagree.
Framework developers are also humans: they might want to check whether their runtime works with MDX.
There are also tools that are not frameworks, but wrap JSX runtimes: think emotion, restyle. They might want to check whether their runtime works with MDX.
And finally there are humans that for other reasons just wrap JSX runtimes, such as for debugging or to learn.

They don’t have a say in how any of it is typed or implemented.

I disagree, I think they do.

I’m saying the status quo (incorrect types) is significantly worse than loose types.

Facebook did not ship types. It was a @ts-expect-error.
Now they ship types. It is a @ts-expect-error.

hast-util-to-jsx-runtime is only used in MDX for its (incorrect) types.

This is intentional: if we can get correct types from another place that is fine too.
I argue that most of the bugs we uncovered in JSX runtimes (svelte, solid, vue, preact) would’ve been improved if there were some good types/docs for the automatic runtime.

Yes, the problem needs to be solved there as well, but there’s no reason to block a fix in MDX for that.

We often implement solutions in the place where the problem originates, instead of patching intermediate packages?

I don’t see how, except for disabling type-coverage, at least for the file that contains the any.

type-coverage supports ignore comments.
type-coverage also supports choosing which files to ignore: you can put anys in a particular file and ignore only that file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
📦 area/deps This affects dependencies ☂️ area/types This affects typings 🤞 phase/open Post is being triaged manually 👶 semver/patch This is a backwards-compatible fix 🐛 type/bug This is a problem
Development

Successfully merging this pull request may close these issues.

Type Error on jsx type with react jsx runtime
3 participants