Describe the bug
When consuming this component:
type Props = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "className">;
const Button = React.forwardRef<HTMLButtonElement, Props>((props) => {
return <button {...props} />
});
resulting in type (as reported by VS Code on hover & by inspecting the compiled d.ts):
const Button: React.ForwardRefExoticComponent<
Pick<
ButtonHTMLAttributes<HTMLButtonElement>,
| "autoFocus"
| "disabled"
| "form"
| "formAction"
| "formEncType"
| "formMethod"
| ... 259 more ...
| "sx"
> &
React.RefAttributes<...>
>;
from another Theme UI-agnostic project it encounters the following type error:
Type '{ children: string; }' is missing the following properties from type 'Pick
, "autoFocus" | "disabled" | "form" | "formAction" | "formEncType" | "formMethod" | "formNoValidate" | ... 258 more ... | "sx">': css, sx TS2739
To Reproduce
npm inpm inpm run start Expected behavior
Consumer isn't affected by Theme UI related types on primitive JSX elements like <button>
Additional context
Omit<> or forwardRef()Omit<> into Pick<>tsdx for the library scaffolding, but I don't think it is related, as my original project is using create-react-libraryWorkaround
You can just omit both props like
type Props = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "className" | "css" | "sx">;
but I find it far from ideal having to account for Theme UI specific props on such a primitive type as it is ButtonHTMLAttributes
but I find it far from ideal having to account for Theme UI specific props on such a primitive type as it is ButtonHTMLAttributes
I would be nice if TypeScript recognized sx and css prop only if jsx imported from Theme UI is set in the pragma comment for current file.
Unfortunately, JSX support in TypeScript isn't perfect. It doesn't work like this, and both Theme UI and Emotion inject its pragma supported prop into all React components.
I reproduced the problem.
Omit is compiled to Pick. (Why? :o)theme-ui is not a dependency of consumer, we don't have sx prop (from Theme UI) nor css prop (from Emotion) types in it.Pick<ButtonHTMLAttributes<HTMLButtonElement>, 'sx'> is unknown. sx was optional, but we don't know it in consumer.I have two workarounds for you.
Add theme-ui as peer dependency.
Theme UI with its 20.3kB gzipped (okay, it's treeshakable but still) may be too big to use as a hidden dependency. Due to component libraries hiding their dep on styling libraries, big applications often end up with multiple styling libs in the bundle (I once had styled-components, emotion and goober).
Since Theme UI uses React context for theming, the theme for your component library can be overriden by your user. I find it nice to be frank about it and advertise it as a feature instead of a surprising bug.
Both React context and JSX types make Theme UI hard to hide.
Make sure Props are not inlined with interface keyword.
Type aliases can be inlined during the build.
We can use interface keyword and export our props. This is generally a good idea. They're public anyway and they can be accessed with React.ComponentProps<typeof Button>, but exporting ButtonProps for convenience is quite nice IMO.
import React, { ButtonHTMLAttributes } from 'react';
export interface ButtonProps
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'className'> {}
export const Button = React.forwardRef<HTMLButtonElement, Props>(props => {
return <button {...props}
});
I'd prefer interface for library public API, because it introduces a named type (which won't get inlined), allows declaration merging, and error messages with them are more succint and thus less scary.
https://www.typescriptlang.org/docs/handbook/advanced-types.html#interfaces-vs-type-aliases
@mxstbr @jxnblk as Theme UI is being used for more component libraries, should the docs include _general not Theme UI specific_ advice for building component libraries? A page in Recipes?
@hasparus the more docs, the better, in my opinion. Feel free to add stuff as you go, even if it's WIP or something we don't link to
This is fixed as of 0.5 (which isn’t released as stable but available nonetheless). Can you test the alpha version?
(Closing as a discussion)
@lachlanjc Just tried it, it works beautifully 🎉
Thanks for your hard work, guys!
@lachlanjc what commit was the fix for this issue? Thanks!
Most helpful comment
@hasparus the more docs, the better, in my opinion. Feel free to add stuff as you go, even if it's WIP or something we don't link to