Hey,
If i use Box and then i apply ref forwarding on a new component called button. It would seem that in typescript when using forward ref it seems that box is set to HTMLDivElement and if i supply HTMLButtonElement we get error that it needs to be a div element, i assume this is to do with the 'as' prop?
Just wandered what i'm doing wrong - i even tried the typings for ForwardRef and it still did not work. TS version 4.0.2
for example:
import * as React from 'react';
import { Box, BoxProps } from 'my-lib';
export interface IButtonProps extends BoxProps {
label: string;
variant?: 'primary' | 'secondary' | 'tertiary';
children: React.ReactNode;
}
const Button = React.forwardRef<
HTMLButtonElement,
IButtonProps,
>(({ label = 'change me', variant = 'primary', ...rest }, ref) => {
return (
<Box
as="button"
ref={ref}
variant={variant}
aria-label={label}
{...rest}
/>
);
});
export { Button };
Type '((instance: HTMLButtonElement | null) => void) | MutableRefObject<HTMLButtonElement | null> | null' is not assignable to type 'string | ((instance: HTMLDivElement | null) => void) | RefObject<HTMLDivElement> | null | undefined'.
Type '(instance: HTMLButtonElement | null) => void' is not assignable to type 'string | ((instance: HTMLDivElement | null) => void) | RefObject<HTMLDivElement> | null | undefined'.
Type '(instance: HTMLButtonElement | null) => void' is not assignable to type '(instance: HTMLDivElement | null) => void'.
Types of parameters 'instance' and 'instance' are incompatible.
Type 'HTMLDivElement | null' is not assignable to type 'HTMLButtonElement | null'.
Type 'HTMLDivElement' is missing the following properties from type 'HTMLButtonElement': disabled, form, formAction, formEnctype, and 13 more.ts(2322)
index.d.ts(143, 9): The expected type comes from property 'ref' which is declared here on type 'IntrinsicAttributes & ClassAttributes<HTMLDivElement> & HTMLAttributes<HTMLDivElement> & Pick<...>
for now my fix is lame but its all i have got ;-)
ref={ref as any}
looking on emotion comments and it looks like the as prop has been thought about by different source - maybe a good reference?
https://github.com/kripod/react-polymorphic-box/blob/main/src/Box.tsx
Thanks in advance ;-)
Hey @MMT-LD :wave: Thanks for the issue!
This is the same problem as mentioned in #1124.
TLDR: Universal as prop and TypeScript are not good friends.
This is a known limitation. It's quite tricky to get
asprop with field correctly working. on Box. Despite merging prop types with > the props of components passed inasprop is supported inField, we stopped at the simplest implementation with Box.
You could work around this
Button exported from theme-ui and @theme-ui/componentsBox.withComponent (it leaks from @emotion/styled, so it's not documented in Theme UI docs).const BoxButButton = Box.withComponent("button")
<button> with JSX pragma Additional context: https://github.com/chakra-ui/chakra-ui/issues/1582
@hasparus thanks for the workarounds. However, i think the JS community seems to be going towards typescript more and more (from looking at other libs and conversations) and i think if theme-ui could support the 'As' prop like Chakra, looks like they made significant progress over the last month, then that would be an amazing feature - otherwise its really only useful for non typescript users. Unless i'm missing the point of Box? or maybe we should be using/consuming the components differently?
Back to your points:
Let me say that i really like this lib and i know how much work everybody puts into keeping a lib like this going 馃憤
could support the 'As' prop like Chakra, looks like they made significant progress over the last month, then that would be an amazing feature
I've spent a bunch of time fiddling with it, and I'm not convinced it's an amazing feature. TypeScript compilation times suffer drastically in large codebases using as prop (I even managed to get an OOM :D). It's a really cool gun to shoot oneself in the foot. I did it, and I'd strongly advise to use sx prop with jsx pragma in TypeScript.
Pretty soon, with React Automatic Runtime, you won't even need /** @jsx jsx */ comment.
Additional context:
@hasparus just wanted to get clarification on withComponent from emotion so i asked the question.
@hasparus Is #1274 enough of a fix for this, or should we do more to fix this?
@lachlanjc, I agree we should describe it in the docs, but we could move on this.
I agree with Andarist (https://github.com/emotion-js/emotion/issues/2012#issuecomment-691938105), that withComponent is quirky API. I think we can afford to deprecate it and remove it, and after we do, we could drop dependency on styled-system and @emotion/styled.
That sounds great, though will be a breaking change in requiring @emotion/styled to be installed separately (I use that on a few projects). I've never used withComponent on a Theme UI project (since using it on styled-components in the past was a nightmare personally & I didn't realize Theme UI even supported it). Plus, removing those two dependencies would noticeably bump down Theme UI package size, which I would be a big fan of.
Most helpful comment
I've spent a bunch of time fiddling with it, and I'm not convinced it's an amazing feature. TypeScript compilation times suffer drastically in large codebases using
asprop (I even managed to get an OOM :D). It's a really cool gun to shoot oneself in the foot. I did it, and I'd strongly advise to usesxprop withjsxpragma in TypeScript.Pretty soon, with React Automatic Runtime, you won't even need
/** @jsx jsx */comment.Additional context:
https://blog.andrewbran.ch/polymorphic-react-components/