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

Extending attributes #1

Open
mindplay-dk opened this issue Sep 14, 2022 · 8 comments
Open

Extending attributes #1

mindplay-dk opened this issue Sep 14, 2022 · 8 comments

Comments

@mindplay-dk
Copy link

Hi,

I'm in way over my head here, and feel free to ignore me if you don't want to spend time on this. 😅

I'd like all attributes to accept their allowed value or a callback function returning the same type - is there any way to do that? I know this would be a union with a mapped type, but I can't figure out what to reference in your types, or where to put it in my own type definitions.

I'd also like to use camel-cased event names, e.g. onClick instead of onclick - but also couldn't figure out how to rename those properties. Again, probably a mapped type using an interface like { onclick: "onClick" } and maybe Omit<...> get remove the lowercase props.

(Would it make sense to have a camel-cased optional type built-into the library? Or even change it. I realize your types are intended to mirror HTML, where these are lowercase, but the camel-case convention is definitely more widely used in libraries, and the underlying HTML attributes are case-insensitive anyhow.)

Of course, I could do everything by just copy/pasting this library and making the changes, but I was hoping not to own a copy of all this generated code. (I could copy and modify your code generators as well, but I'd prefer not to.)

As said, I don't really expect "free technical support" here, so don't answer unless you feel like it. 🙂

Cheers!

@stagas
Copy link
Owner

stagas commented Sep 14, 2022

Hey,

Currently this project is frozen as it meets my current needs (other than the occasional patch), but feel free to fork and extend it as you like. It is really meant to be a bare minimum for html/svg to be copy-pasteable as jsx, along with MDN docs and types. The data used to generate these types have become incompatible so a lot would have to be rewritten anyway if i were to add features here. But for just those two features, the place to start would be the generator files here https://github.com/stagas/html-jsx/tree/main/scripts and npm run gen to recreate them.

Good luck :) cheers

@mindplay-dk
Copy link
Author

It is really meant to be a bare minimum for html/svg to be copy-pasteable as jsx

Ah, I see.

I was hoping the intention with this was to provide a common library of types for JSX projects. It's a shame how that doesn't really seem to exist. Every project out there (React, Preact, Solid) either generates or copy/pastes and maintains this by hand.

Can you say what motivated you to do this?

I mean, technically, all the element types and properties already have defined types built into the standard TypeScript libraries - and it would be possible to derive the types from these, right?

I suspect the problem is documentation? This issue:

microsoft/TypeScript#49909

I really don't want to own/maintain/ship a copy of the entire HTML spec. But it looks basically every UI library out there is forced to do that. I wouldn't want to create let alone publish even a tiny UI library if I have to own this amount of types - that's bad enough that I'll probably just shelve my little toy project again and leave it until maybe someday this situation improves in TS. 😅

Just wondering what drove you to this point?

@stagas
Copy link
Owner

stagas commented Sep 16, 2022

I was hoping the intention with this was to provide a common library of types for JSX projects. It's a shame how that doesn't really seem to exist. Every project out there (React, Preact, Solid) either generates or copy/pastes and maintains this by hand.

That was the original goal, but it's difficult.

Can you say what motivated you to do this?

I mean, technically, all the element types and properties already have defined types built into the standard TypeScript libraries - and it would be possible to derive the types from these, right?

No because they're not of the same type or have the same naming. You are going to have to generate from a different source.

I really don't want to own/maintain/ship a copy of the entire HTML spec. But it looks basically every UI library out there is forced to do that. I wouldn't want to create let alone publish even a tiny UI library if I have to own this amount of types - that's bad enough that I'll probably just shelve my little toy project again and leave it until maybe someday this situation improves in TS. sweat_smile

Just wondering what drove you to this point?

TS treats JSX specially, making it difficult to separate the JSX types from the vdom library, so I didn't want to copy paste the types in every project i use them. This was made to make them reusable so i can just require and add the minimum amount of code in order to get types and documentation for a jsx lib. I could have used React's or the derivatives but they were lacking documentation and so i figured i could generate it from a better source, hence this project. Whatever it is, it's a step towards that direction, someone needs to do the rest of the work :) meaning integrate with the latest spec data, add generators for SVG, ARIA etc, build flavors for both camelCased and html attributes and then it could become a package that anybody could use. If i ever get back to this that's the plan at least, but for the time being it's meeting my needs and there is ton more work that needs attention rn :)

@mindplay-dk
Copy link
Author

...it would be possible to derive the types from these, right?

No because they're not of the same type or have the same naming

Not the same type?

I mean, the HTMLImageElement was generated from the same IDL sources as your ImgHTMLAttributes, weren't they?

Filter the readonly properties and the methods, and you should have all the same props as what we're programming against with regular DOM elements, shouldn't we?

And there's HTMLElementTagNameMap linking the element names to their types.

I actually wonder if maybe it would be better to go directly to the source and help out with TypeScript's official types - using inheritance to create separate types for all the setters should make it possible for everyone to use those types and derive most of what they need.

Although there's still the documentation issue...

@stagas
Copy link
Owner

stagas commented Sep 17, 2022

...it would be possible to derive the types from these, right?

No because they're not of the same type or have the same naming

Not the same type?

Besides the difference having camelCased attribute naming which is going to be a problem with SVG at least, since it's not case insensitive, in the numerical attributes in JSX we also accept strings, so x=100; ... <img width={x}> and <img width="100"> are both valid. Properties are also getters + setters, but JSX attributes are only setters and the documentation reflects that. Attributes are also all optional in HTML/JSX, but they're not optional in the instance's interface.

Another big difference are the event handlers, in TS they are defined as (this: WindowEventHandlers, ev: Event) => any) which means anything goes, in this project they are generic EventHandler<T, E> where T is the HTML element instance and E is the event. TS will never make the event handlers generic because it slows their benchmarks by a lot, meaning performance reasons don't allow it for general use and until the speed is improved somehow magically you can't have safety and inferrence for the event handlers, as you can do with these types here.

These and perhaps a few more small details, don't allow a 1-1 match of the HTML element instance type to the HTML tag/JSX attributes, so i don't see a way they can be generalized for both use cases. They are different domains, they just happen to share a lot of definitions but they're not exactly compatible. You will need to use a separate types file for JSX.

@mindplay-dk
Copy link
Author

Regarding properties vs attributes, if I understand you correctly, the goal of this project is to support HTML attributes such as <img width="100">, as well as properties like <img width={x}> where x is a number.

How come? I mean, who needs/wants the string attribute version in a JSX context?

From everything I've seen with JSX, for intrinsic elements, the type usually maps to document.createElement(type), and props generally get applied to object properties by default, with a fallback to setAttribute when there's no property you can map to.

Attributes generally are less performant (have to do type conversions) and less safe (for type conversions that fail) and so I don't know why you would need/want them in a JS context.

Not trying to be argumentative here 😅 just genuinely wondering what your use case is? 🙂

@stagas
Copy link
Owner

stagas commented Sep 18, 2022

I create an .html file in isolation, create a thing that i like, now i want to add functionality to it. So i create a .tsx file, copy paste that chunk of HTML/SVG/CSS into a function and it works, no modifications. No setup, no changing attribute names, no weird state/styles. Raw HTML+CSS and Custom elements because those are the real interfaces. Seamless integration of the code and its target output. What i see in my JSX function is exactly what i see when i inspect the component in the Elements panel in DevTools. I can even edit them right there, change attributes, examine different behavior without going back to the editor. This approach, making HTML first class as JSX instead of just treating it only as a render target like React does, has been very successful to me productivity wise mainly because there's less magic and stuff are more predictable.

@mindplay-dk
Copy link
Author

Well, I guess that's a matter of perspective. From my point of view, HTML is just a document format, an encoding - whereas, after the document has been loaded, the "real interface" is the resulting object model, in which attributes don't really matter.

I can see how being able to copy/paste is convenient, but I can't see that always working? I'm pretty sure you're not writing your event handlers as inline JS code, as text in attributes? 😅

Anyhoo, it explains why you have the attributes in there, and each to his own, I guess. ✌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants