-
-
Notifications
You must be signed in to change notification settings - Fork 184
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
Typescript support for extended Styled Components #38
Comments
attn: @kingdaro |
I don't really feel personally motivated to implement this. Firstly because it's not documented(?), and ideologically, I feel like using this a lot would lead down an inheritance-heavy path over that favoring composition. An example in case I'm unclear: // these are only a few classes, but imagine they're way longer, since buttons generally are
const Button = tw.button`...`
const PinkButton = tw(Button)`bg-pink`
const UnderlineButton = tw(Button)`underline`
const PinkUnderlineButton = tw(/* ??? */)` ??? `
// I'd prefer this
const pinkButtonStyle = `bg-pink`
const underlineButtonStyle = `underline`
// then apply them to any element/component
// don't have to make new styled components, or fool around with 'as' prop
<button css={pinkButtonStyle} />
<a css={underlineButtonStyle} />
<Link to="/" css={[pinkButtonStyle, underlineButtonStyle]} /> There are ways around the diamond problem I've pointed out, but with the alternate path here, it's something I don't even have to think about, and I think it aligns more with Tailwind's core philosophy. That aside, if someone wants to pick up the mantle, I'll gladly review the PR, provided the type tests are also updated |
Thanks for your thoughts on this Daro. Edit: I've now documented the wrapping in feature in the readme. |
+1 |
I'm keen on adding this but not sure where to start. |
It appears that emotion has an issue related to this that will be fixed in v11: emotion-js/emotion#1823 Perhaps best to look at how v11 handles the types: // Definitions by: Junyoung Clare Jang <https://github.com/Ailrun>
// TypeScript Version: 3.2
import * as React from 'react'
import { ComponentSelector, Interpolation } from '@emotion/serialize'
import { PropsOf, DistributiveOmit, Theme } from '@emotion/react'
export {
ArrayInterpolation,
CSSObject,
FunctionInterpolation
} from '@emotion/serialize'
export { ComponentSelector, Interpolation }
/** Same as StyledOptions but shouldForwardProp must be a type guard */
export interface FilteringStyledOptions<
Props,
ForwardedProps extends keyof Props = keyof Props
> {
label?: string
shouldForwardProp?(propName: PropertyKey): propName is ForwardedProps
target?: string
}
export interface StyledOptions<Props> {
label?: string
shouldForwardProp?(propName: PropertyKey): boolean
target?: string
}
/**
* @typeparam ComponentProps Props which will be included when withComponent is called
* @typeparam SpecificComponentProps Props which will *not* be included when withComponent is called
*/
export interface StyledComponent<
ComponentProps extends {},
SpecificComponentProps extends {} = {}
> extends React.FC<ComponentProps & SpecificComponentProps>, ComponentSelector {
withComponent<C extends React.ComponentType<React.ComponentProps<C>>>(
component: C
): StyledComponent<ComponentProps & PropsOf<C>>
withComponent<Tag extends keyof JSX.IntrinsicElements>(
tag: Tag
): StyledComponent<ComponentProps, JSX.IntrinsicElements[Tag]>
}
/**
* @typeparam ComponentProps Props which will be included when withComponent is called
* @typeparam SpecificComponentProps Props which will *not* be included when withComponent is called
*/
export interface CreateStyledComponent<
ComponentProps extends {},
SpecificComponentProps extends {} = {}
> {
/**
* @typeparam AdditionalProps Additional props to add to your styled component
*/
<AdditionalProps extends {} = {}>(
...styles: Array<
Interpolation<
ComponentProps &
SpecificComponentProps &
AdditionalProps & { theme: Theme }
>
>
): StyledComponent<ComponentProps & AdditionalProps, SpecificComponentProps>
(
template: TemplateStringsArray,
...styles: Array<
Interpolation<ComponentProps & SpecificComponentProps & { theme: Theme }>
>
): StyledComponent<ComponentProps, SpecificComponentProps>
/**
* @typeparam AdditionalProps Additional props to add to your styled component
*/
<AdditionalProps extends {}>(
template: TemplateStringsArray,
...styles: Array<
Interpolation<
ComponentProps &
SpecificComponentProps &
AdditionalProps & { theme: Theme }
>
>
): StyledComponent<ComponentProps & AdditionalProps, SpecificComponentProps>
}
/**
* @desc
* This function accepts a React component or tag ('div', 'a' etc).
*
* @example styled(MyComponent)({ width: 100 })
* @example styled(MyComponent)(myComponentProps => ({ width: myComponentProps.width })
* @example styled('div')({ width: 100 })
* @example styled('div')<Props>(props => ({ width: props.width })
*/
export interface CreateStyled {
<
C extends React.ComponentType<React.ComponentProps<C>>,
ForwardedProps extends keyof React.ComponentProps<
C
> = keyof React.ComponentProps<C>
>(
component: C,
options: FilteringStyledOptions<PropsOf<C>, ForwardedProps>
): CreateStyledComponent<Pick<PropsOf<C>, ForwardedProps> & { theme?: Theme }>
<C extends React.ComponentType<React.ComponentProps<C>>>(
component: C,
options?: StyledOptions<PropsOf<C>>
): CreateStyledComponent<PropsOf<C> & { theme?: Theme }>
<
Tag extends keyof JSX.IntrinsicElements,
ForwardedProps extends keyof JSX.IntrinsicElements[Tag] = keyof JSX.IntrinsicElements[Tag]
>(
tag: Tag,
options: FilteringStyledOptions<JSX.IntrinsicElements[Tag], ForwardedProps>
): CreateStyledComponent<
{ theme?: Theme },
Pick<JSX.IntrinsicElements[Tag], ForwardedProps>
>
<Tag extends keyof JSX.IntrinsicElements>(
tag: Tag,
options?: StyledOptions<JSX.IntrinsicElements[Tag]>
): CreateStyledComponent<{ theme?: Theme }, JSX.IntrinsicElements[Tag]>
}
declare const styled: CreateStyled
export default styled |
As a temporary solution we can declare a modified module: // twin.d.ts
import { ComponentType } from 'react';
import { TwFn, TemplateFn } from 'twin.macro';
declare module 'twin.macro' {
type TwComponentWrapper = <T extends ComponentType<any>>(component: T) => TemplateFn<T>;
const tw: TwFn & TwComponentMap & TwComponentWrapper;
export = tw;
} @ben-rogerson, how about updating the type with this? |
Thanks for looking into this - it all looks good at first glance. I'll aim to release a patch after some testing. |
Works great for me so far! Thanks! |
I've put up a PR if anyone wants to take a squiz before I merge. |
Update to 1.6.0 and all should be good now 🎉 |
Hi,
I've been using the macro to extend components and have recently started also using Typescript and I'm getting errors from the
tsc
when using extended components:So, this works in regular JSX:
but when it becomes TSX then I get this error:
The workaround for this is to import
styled
and call it like this:Which is great for now but everywhere else I'm using
tw.div
etc and would like to be able to consistently usetw
instead of having to importstyled
when extending components.#24 fixed other issues but there's still this case.
The text was updated successfully, but these errors were encountered: