styled-components / styled-components

Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress 💅
https://styled-components.com
MIT License
40.11k stars 2.48k forks source link

fix(types): allow using a styled component as a key inside object styles in the web runtime #4249

Closed iiroj closed 1 week ago

iiroj commented 4 months ago

In v6 it's currently not possible to use a styled-component as a key inside object styles to reference its class name, because TypeScript is not happy about:

A computed property name must be of type 'string', 'number', 'symbol', or 'any'.

This PR adds the same hack available in the older @types/styled-components package intersecting the main IStyledComponent type with the string type, available only in the web runtime.

Basically, this becomes possible:

const H1 = styled.h1({
  fontSize: '2rem'
})

const Header = styled.header({
  [H1]: {
    marginBottom: '1rem'
  }
})

I realize this can be worked around by wrapping they key in either:

  1. H1.toString()
  2. String(H1)
  3. a template string ${H1}

but in my opinion it's a bit annoying.

Would be willing to accept this PR? What kind of additional testing would this require? Currently I have developed this workaround locally inside a project using TypeScript and styled-components. I did not test if it affects the React Native runtime, but it shouldn't.

iiroj commented 4 months ago

The hack can be seen here: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/2c3935e7d9bd5853dd3a8c54ede337a3c652f4bf/types/styled-components/index.d.ts#L131-L133

quantizor commented 1 week ago

Slight modification:

interface IStyledComponentBase<R extends Runtime, Props extends object = BaseObject>
  extends PolymorphicComponent<R, Props>,
    IStyledStatics<R, Props>,
    StyledComponentBrand {
  defaultProps?: (ExecutionProps & Partial<Props>) | undefined;
  toString: () => string;
}

export type IStyledComponent<
  R extends Runtime,
  Props extends object = BaseObject,
> = IStyledComponentBase<R, Props> & (R extends 'web' ? string : {});

interface tends to be a lot snappier than type where you can get away with it