Skip to content

Typing errors when using @headlessui/react with preact #4072

@kurtbuilds

Description

@kurtbuilds

This repo contains a very basic react app, using a Disclosure component from @headlessui/react:

https://github.com/kurtbuilds/react-headlessui.git

Run tsc on that repo, and no errors are reported.

This identical project is using preact. The app functions as intended, but typescript (incorrectly) reports errors:

https://github.com/kurtbuilds/preact-headlessui-issue.git

The (first) error is Type 'string' is not assignable to type '(bag: DisclosureRenderPropArg) => string'. on the className property of the Disclosure jsx component.

Brief background on headlessui: the headlessui components are generic on the tag property (e.g. it renders in HTML as a <nav> element if you use the property as="nav"). Accordingly, the type is generic on that, and for some reason, I think those generics aren't working with preact right now. Following the full logic chain from headlessui/react:

Disclosure has the type defined here: https://github.com/tailwindlabs/headlessui/blob/076b03cf491629b1930531c4ca616902be6c1688/packages/%40headlessui-react/src/components/disclosure/disclosure.tsx#L430

interface ComponentDisclosure extends HasDisplayName {
  <TTag extends ElementType = typeof DEFAULT_DISCLOSURE_TAG>(
    props: DisclosureProps<TTag> & RefProp<typeof DisclosureFn>
  ): JSX.Element
}

and DisclosureProps ultimately gets the className property from ClassNameOverride in the Props type here: https://github.com/tailwindlabs/headlessui/blob/076b03cf491629b1930531c4ca616902be6c1688/packages/%40headlessui-react/src/types.ts#L48

If I manually test out those types, their logic is definitely working (also confirmed by it working in React), so I think the issue is something about the generic not working correctly.


I tried manually editing the ClassNameOverride type to see if there are any errors that crop up once this one is fixed. Unforunately, there's another error hidden behind this one.

TS2322: Type '{ children: ({ open }: { open: boolean; }) => Element; as: string; className: string; }' is not assignable to type 'IntrinsicAttributes & CleanProps<string, never> & OurProps<string, DisclosureRenderPropArg> & { ...; } & { ...; } & { ...; }'.   Type '{ children: ({ open }: { open: boolean; }) => Element; as: string; className: string; }' is not assignable to type 'CleanProps<string, never>'.     Property 'children' is incompatible with index signature.       Type '({ open }: { open: boolean; }) => JSXInternal.Element' is not assignable to type 'never'.

Fortunately, I think this is a similar issue to the previous one. The stated error is about the children of the component (the child being a function ({open}) => JSXInternal.Element) is not assignable to never, which appears to come from a generic parameter from CleanProps. And I think that generic should be taking a value, rather than be never-valued.

To confirm that latter point, if you make a typo in the function in the react version of the project, you'll get an error: TS2339: Property 'ope' does not exist on type '{ open: boolean; }'., which confirms that the expected value for children of the Disclosure component, in the react version, does get correctly typed as ({open} => JSXInternal.Element.

I've managed to workaround this issue for now by remapping the types of the components as any:

import {
    Disclosure as HDisclosure,
} from '@headlessui/react'

export const Disclosure: any = HDisclosure;

but obviously that eliminates any benefit from using typescript. If there are better workarounds until this gets solved, I'm curious to hear them.


I originally discussed this issue in #4068

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions