kitajs / html

🏛️ Super fast JSX runtime to generate HTML strings that works everywhere. If it supports string we got you covered.
http://kitajs.org/discord
MIT License
567 stars 31 forks source link

JSX.Element definition lacks some types that do compile. #84

Closed Nmans01 closed 9 months ago

Nmans01 commented 9 months ago

Prerequisites

Versions

@elysiajs/html@0.7.3, I understand this is built using this package.

Description

The current JSX.Element definition causes errors to be thrown when including some types in JSX: number | boolean | null | undefined. However, JSX such as the following...

<div>
    {1}
    {undefined}
    { possiblyFalse() && "Hi mom" }
</div>

...runs as expected despite throwing IDE type errors.

Steps to Reproduce

See description

Expected Behavior

See description; expected no type errors to be thrown for given types.

arthurfiorette commented 9 months ago

Hey @Nmans01 what is thrown? I need more details than "causes errors to be thrown"... What line throws the error in your example?

Please provide a useful reproducible example

Nmans01 commented 9 months ago

Sorry, there was a wrongful assumption there. The provided example using a div was a simplification of what I had originally.

Where I had actually encountered the error was within once of my own defined TSX components. I use these types to define my components:

export type Component<P = {}> = (props: P) => JSX.Element;
export type ParentComponent<P = {}> = (props: P & { children?: JSX.Element | JSX.Element[] }) => JSX.Element;

I migrated to Elysia from a stack that used SolidJS, and this is how SolidJS had defined its components. This typing causes everything to work as expected for the most part. However, it fails for my actual example, which uses a ParentComponent, which could arbitrarily be defined as:

const MyComponent : ParentComponent<{}> = (props) => {
    return <div>{props.children}</div>;
}

The error arises when I use this component and try to pass in some non-string type like number:

// Works
<MyComponent>
    {"Some string"}
</MyComponent>

// Does not work
<MyComponent>
    { 0 }
</MyComponent>

// Does not work, since the type is possibly false
<MyComponent>
    { myVal == "something" && "hi mom" }
</MyComponent>

The error in every failing case is simple, just something like Type 'undefined' is not assignable to type 'Element'. The reason I wrote this issue is that all of these cases compile and execute properly, the issue is just that Element does not include these types in its definition and so there is an IDE typescript error. The fix would just be to append JSX.Element to include the additional types I included in the original comment.

arthurfiorette commented 9 months ago

That's why we provide Html.PropsWithChildren, you are simply using the wrong type. Please use Html.Component type instead of your own Component/ParentComponent. If you do not need the children type, manually typing it everywhere is less verbose than using your Component type.

Example:

export function CompWithChild(props: Html.PropsWithChildren<MyType>) {
  return <>{props.children}</>;
}

export function CompWithoutChild(props: MyType) {
  return <>Hello</>;
}

A tag child type (Html.Child) has a different parsing and usage from JSX.Element (the typename JSX requires to work).

You can use all of these values as children:

https://github.com/kitajs/html/blob/b2e066952e14fdc889b62bdadd4543585f377ace/index.d.ts#L165-L172

But only string | Promise<string> is the valid result type of a <tag />.

https://github.com/kitajs/html/blob/b2e066952e14fdc889b62bdadd4543585f377ace/jsx.d.ts#L19

Nmans01 commented 9 months ago

I hadn't seen the type Html.PropsWithChildren. That's what I was looking for.