-
-
Notifications
You must be signed in to change notification settings - Fork 3
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
Base types on generics, not global JSX namespace #6
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When did this change? Which TS versions can(not) work without JSX
?
@@ -108,6 +108,7 @@ | |||
"ignoreFiles": [ | |||
"example/**/*.js" | |||
], | |||
"ignoreNested": true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise type-coverage
reports nested any
types on types that we don’t control, nor where we care at all about its impact. E.g. ReactElement
.
lib/index.js
Outdated
*/ | ||
|
||
/** | ||
* @typedef {JSX.Element | string | null | undefined} Child | ||
* @template JSXElement | ||
* @typedef {JSXElement | string | null | undefined} Child |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
template
is sometimes defined before and sometimes after. Make this consistent.
And, can you add descriptions to them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I first ran into some weird behaviour regarding order. Splitting into multiple block comments seems to have fixed everything.
Do you have a preference where they are added? I’ll add descriptions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, for some reason the template
s are “per block” 🤷♂️
Ehh, consistency is most important. I think I recently liked before more, but not 100%, could’ve been the other way around!
lib/index.js
Outdated
*/ | ||
|
||
/** | ||
* @typedef {JSX.Element | string | null | undefined} Child | ||
* @template JSXElement |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding required type parameters is a breaking change.
Is that worth it? Should there be defaults?
Also, all these types are documented in the readme but this PR does not change the docs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The types of lib/index.js
are private. It’s ok to make such changes here.
index.js
exposes the public interface. The addition of required generics there is indeed a breaking change. Also the Components
and ExtraProps
types are removed. We’d better discuss whether or not this change is breaking based on those changes.
I suspect the breaking impact is low, but this is breaking indeed on the type level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I posted here because it was the first, and Child
is in Props
.
As I noted somewhere else, these APIs are supposed to be used by people. They need to be documented in the readme and learned by humans.
Also, about what to discuss, see my last comment: #6 (comment).
*/ | ||
|
||
/** | ||
* @typedef {JSX.Element | string | null | undefined} Child | ||
* @template JSXElement | ||
* @typedef {JSXElement | string | null | undefined} Child | ||
* Child. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How useful is a Child<X>
type if all it does is add X
to an enum of 3 primitive values? Could it be replaced by X | Child
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It’s used several times. I think it’s ok to keep.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These types are exposed from this project. They are meant to be used and they are documented for humans to learn. They can be abused (someone could use it with any value that is unrelated to JSX). PrimitiveChild | MyJsxElement
seems like a better API to me than Child<AnyCustomValue>
lib/index.js
Outdated
* Fragment symbol. | ||
* @property {Array<Parents>} ancestors | ||
* Stack of parents. | ||
* @property {Partial<Components>} components | ||
* @property {Record<string, JSXElementType>} components |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It’s a bit sad that here we loose the info on blockquote
meaning particular props and corresponding components though, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I find this unfortunate too. I’m not sure where this is used. The Components
type doesn’t seem to be exposed via @mdx-js/mdx
, so I think the impact of this in the end is very minimal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This project is not used in MDX I believe. It’s a separate tool that can be used by anyone. It’s also used in react-markdown
and in rehype-react
. We’d loose support there too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You’re right. I thought it was. Only some types are used (for evaluate
).
that sounds pretty nice 🤔 why not? |
Only really old TypeScript versions can’t work without the global Although I recommend against using the global declare module 'mdx/types' {
import { JSX } from 'preact/jsx-runtime'
}
It does sound nice! I ran into some JSDoc limitations though, that make it impossible to make this work without converting |
084384b
to
e661865
Compare
I was about to open an issue requesting that the types (FunctionalComponent, etc) from |
Those types depend on the global Note that you only need framework agnostic JSX types if you write something where those types become part of the public interface of a library. You can likely use |
That's exactly what I'm doing. I'll have compat between React, Preact, and SolidJS when done. |
But will those JSX based types be part of the public interface? Anyway, that should also be possible with the generics solution proposed in this PR. |
Yes.
Would love to see an example using the changes in the PR |
It sounds like we could use that registry here to type which components are allowed?
Curious to hear more about those limitations.
a) we might be able to support both? b) many runtimes do |
For a while now, JSX frameworks don’t have to declare the global `JSX` namespace anymore. This is a significant improvement when using multiple JSX frameworks in the same project. React has deprecated the global `JSX` namespace. Preact doesn’t even define it anymore. This change replaces the use of the `JSX` namespace with generics, to make it compatible with modern JSX frameworks.
That was for the classic runtime, but even if we used that, we can’t access that. For the automatic runtime, we need the
I did get it to work after all. Default type parameter values behave somewhat different in types in JSDoc. This was confusing. |
I don’t understand. We can access it, but we can’t?
Do you have an example of how that works? I don’t think I’ve seen Preact or so expose those things. |
It’s not possible to access types in a namespace from the namespace JSX {
interface Element {}
} … it is not possible to retrieve
import { JSX } from 'react/jsx-runtime'
// ^^^ We would need this value
// ^^^^^ But this part is dynamic in our case. So we cannot infer anything from the |
* @typedef {unknown} Components | ||
* @typedef {unknown} ExtraProps |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest removing these stubs.
But like. Which frameworks have removed them? Are frameworks going to? TS itself can validate whether Take https://github.com/preactjs/preact/blob/a2c12f5a46a70b2b58517f5e14e731a77d6d64a3/test/ts/custom-elements.tsx#L3 for example. I find this really hard to review: there are no sources cited for all of this. |
The global Unfortunately demonstrating this doesn’t work well in a playground. Instead, you can try with these files: // package.json
{
"type": "module",
"dependencies": {
"preact": "^10.0.0",
"solid-js": "^1.0.0",
"typescript": "^5.0.0",
"vue": "^3.0.0"
}
} // tsconfig.json
{
"compilerOptions": {
"module": "nodenext",
"jsx": "react-jsx",
"jsxImportSource": "preact",
"noEmit": true
}
} // index.tsx
// These imports exist to show none of them register a global `JSX` namespace anymore.
import 'preact'
import 'solid-js'
import 'vue'
import {/* JSX */} from 'preact/jsx-runtime'
import {/* JSX */} from 'solid-js/jsx-runtime'
import {/* JSX */} from 'vue/jsx-runtime'
type JSXElement = JSX.Element
const element = <div>hello</div> The type of For TypeScript exporting the
So basically the current state is:
Footnotes |
Do you have references? The docs still state that
🤔 The current existing JSX namespace is pretty good? 🤔 You mean that the types of the
We could if folks pass the OK, I think I understand the concerns. The |
This PR deprecates usage in
The docs don’t mention where the I don’t even see a mention of added support for
I mean this in the sense that using Preact or Vue in tests would have shown the problem being solved here.
Yes, that’s the intention of this PR.
What do you mean by We could return |
Yeah I know the one where DT maintainers change something in the React types. But there is no discussion about it. You make a lot of statements about how things are and that Preact/Vue/etc changed something. But there’s nothing backing that up yet.
Right, so I miss sources for such statements. There’s only a change in DT for React. Nothing on the TS part. Or other frameworks.
From where does it infer? From the |
I think this PR removes too many useful features. JSX is great. JSX is used in millions of files. It’s super useful to type components and return values. See also #9 (comment). I would be open to discussing bare minimal |
Initial checklist
Description of changes
For a while now, JSX frameworks don’t have to declare the global
JSX
namespace anymore. This is a significant improvement when using multiple JSX frameworks in the same project. React has deprecated the globalJSX
namespace. Preact doesn’t even define it anymore.This change replaces the use of the
JSX
namespace with generics, to make it compatible with modern JSX frameworks. I also have an alternative branch locally which accepts only one generic, which is the type of thejsx
function.This removes the inference of props in custom components. I’m not sure what the impact is of that.
This could be considered a breaking change on the type level.
Closes #4