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

Incorrect attribute typing for van_jsx #142

Closed
yahia-berashish opened this issue Oct 12, 2023 · 14 comments · Fixed by #166
Closed

Incorrect attribute typing for van_jsx #142

yahia-berashish opened this issue Oct 12, 2023 · 14 comments · Fixed by #166

Comments

@yahia-berashish
Copy link
Contributor

This is another JSX type issue I haven't noticed in the previous issue.
The attributes used for the JSX elements in jsx-internal.d.ts are plain HTML attributes, which means that reactive states cannot be used as attributes = no reactive attributes.
This can be solved by creating a utility type to modify the attributes, something like this:

// modify attribute type
type VanAttrs<T> = {...}

// use utility type instead of HTMLAttributes
 export interface IntrinsicElements {
    a: VanAttrs<HTMLAnchorElement>;
    abbr: VanAttrs<HTMLElement>;
    address: VanAttrs<HTMLElement>;
    area: VanAttrs<HTMLAreaElement>;
    article: VanAttrs<HTMLElement>;
    ...
}

I will probably take a look at this issue later if it is still open

@Tao-VanJS
Copy link
Member

@cqh963852, FYI.

@yahia-berashish
Copy link
Contributor Author

By the way, how to test the VanJS codebase @Tao-VanJS ?

@Tao-VanJS
Copy link
Member

By the way, how to test the VanJS codebase @Tao-VanJS ?

Here is the development steps for the changes in the core VanJS library: #37.

It's not a polished process yet. The external contribution that is complex enough which needs running test to iterate is rare. For small changes, you can just send out the PR for source files and I can take care of the rest (including the testing).

@cqh963852
Copy link
Contributor

export interface IntrinsicElements {
a: HTMLAttributes<HTMLAnchorElement>;
abbr: HTMLAttributes<HTMLElement>;

There is already a utility type HTMLAttributes here.

van_jsx is based on van but the interface is not equal to van, so VanAttrs is not recommended here.


How about the following modification? This can reduce the code length of internal and provide a most basic tag type.

export type Tags = {
  readonly a: HTMLAnchorElement;
  readonly abbr: HTMLElement;
 ...
  readonly svg: SVGSVGElement;
  readonly animate: SVGAnimateElement;
...
};
  type InnerElement = {
    [K in keyof Tags]: Tags[K] extends HTMLElement
      ? HTMLAttributes<Tags[K]>
      : Tags[K] extends SVGElement
      ? SVGAttributes<Tags[K]>
      : never
  };

  export interface IntrinsicElements extends InnerElement {}

@yahia-berashish
Copy link
Contributor Author

yahia-berashish commented Oct 13, 2023

But how to handle attributes that are not HTML but handled by Van?

const text = van.state("");

<input 
// reactive state binding throws a type error since the input element is typed with plain HTML attributes and expects a string
value={text} 
oninput={(e) => text.val = e.target.value}
/>

The VanAttributes type will be simply a modified HTMLAttributes/SVGAttributes where the attributes values can be either primitives or VanJS states.
And also, using a Record<string, ...> in van.d.ts to handle the attributes passed to tags isn't ideal.
My recommendation is for a typed interface for all the HTML tags to be created in van.d.ts replacing the current Tags interface, which can be imported and used as is in the van_jsx directory - it already has some types that should be centralized in the core.

@cqh963852
Copy link
Contributor

Can you post a picture of your error?

I'm not sure if you mean

value:string -> value:State<string>

@yahia-berashish
Copy link
Contributor Author

yahia-berashish commented Oct 13, 2023

Sure

Screenshot 2023-10-13 at 6 21 38 AM

Attributes should support states for reactive binding

@cqh963852
Copy link
Contributor

cqh963852 commented Oct 13, 2023

Sure

Screenshot 2023-10-13 at 6 21 38 AM Attributes should support states for reactive binding

You can create a OrState utility

type OrState<T extends Element> = {
  [K in keyof Element]: Element[K] extends "" ? Element[K] | State<Element[K]> : Element[K]
}
export type Tags = {
  readonly a: HTMLAnchorElement;
  readonly abbr: HTMLElement;
 ...
  readonly svg: SVGSVGElement;
  readonly animate: SVGAnimateElement;
...
};

type InnerElement = {
    [K in keyof Tags]: Tags[K] extends HTMLElement
      ? OrState<HTMLAttributes<Tags[K]>>
      : Tags[K] extends SVGElement
      ? OrState<SVGAttributes<Tags[K]>>
      : never
  };

export interface IntrinsicElements extends InnerElement {}

@yahia-berashish
Copy link
Contributor Author

What do you say about modifying the van.d.ts file @Tao-VanJS ?

@cqh963852
Copy link
Contributor

What do you say about modifying the van.d.ts file @Tao-VanJS ?

Did you mean "What are your thoughts on modifying xx"

@yahia-berashish
Copy link
Contributor Author

But how to handle attributes that are not HTML but handled by Van?

const text = van.state("");

<input 
// reactive state binding throws a type error since the input element is typed with plain HTML attributes and expects a string
value={text} 
oninput={(e) => text.val = e.target.value}
/>

The VanAttributes type will be simply a modified HTMLAttributes/SVGAttributes where the attributes values can be either primitives or VanJS states.
And also, using a Record<string, ...> in van.d.ts to handle the attributes passed to tags isn't ideal.
My recommendation is for a typed interface for all the HTML tags to be created in van.d.ts replacing the current Tags interface, which can be imported and used as is in the van_jsx directory - it already has some types that should be centralized in the core.

I am referring to this comment.

And also, using a Record<string, ...> in van.d.ts to handle the attributes passed to tags isn't ideal.

@Tao-VanJS
Copy link
Member

What do you say about modifying the van.d.ts file @Tao-VanJS ?

There are tens of different element classes in DOM, and each class has tens of properties. Hardcoding the interface of properties for each element class will significantly bloat the size of van.d.ts. Also, the props in tag functions also allow users to specify HTML attributes, where the user should be able to specify for any name.

@yahia-berashish
Copy link
Contributor Author

yahia-berashish commented Oct 18, 2023

What I was thinking of actually is to use the built-in HTML attributes types, but with modifying the values to include things like states and binding functions @Tao-VanJS
Then we can just use the provided vanjs-core types in vanjs-jsx instead of using two separate type systems for attributes like the case now @cqh963852

The van.d.ts code now looks something like this:

...

export type PropValue<TProp = any> = TProp | StateView<TProp> | (() => TProp);

export type Props<TElement extends Element> = Partial<{
  [K in keyof TElement]: PropValue<TElement[K]>;
}>;

export type VanElement<TElement extends Element> = (
  first?: Props<TElement> | ChildDom,
  ...rest: readonly ChildDom[]
) => TElement;

export type Tags = {
  [K in keyof HTMLElementTagNameMap]: VanElement<HTMLElementTagNameMap[K]>;
};

// the rest of the file
...

These modifications will significantly improve typing in vanjs-core, and synchronize the types with vanjs-jsx.

@yahia-berashish
Copy link
Contributor Author

This PR should fix the issues, the modifications are pretty small and simple.

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

Successfully merging a pull request may close this issue.

3 participants