Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typing the css function in styled-components

I'm following the example in the docs for creating media templates, and I'm really struggling to type the arguments to pass to the css function (plain JS version from example):

const sizes = {
  desktop: 992
}

const media = Object.keys(sizes).reduce((acc, label) => {

  acc[label] = (...args) => css`    // <----- how to type args

    @media(max-width: ${sizes[label]}px) {
      ${css(...args)}
    }

  `

  return acc
}, {})

In case you know TS but not styled-components, args is a tagged template literal, so I would use the media object as such:

media.desktop`
  background-color: blue;
  ${variable}
`

I have tried to type args as TemplateStringsArray but TS complains because spread arguments need to be of array type (which I think it is but somehow it is not recognised). If I change the type to TemplateStringsArray[], the css() function complains because it expects at least 1 argument, but received 0 or more.

like image 545
Thomas Chia Avatar asked Jun 02 '26 22:06

Thomas Chia


2 Answers

The signature for a tagged template should be (literals: TemplateStringsArray, ...placeholders: any[]) => string where literals are the strings in the template and placeholders are the variable values.

If you just want to pass all arguments to the css you can use call. Typescript will not let you spread directly because css has required arguments that the ts compiler tries to check for:

acc[label] = (...args: any[]) => css`  
  @media(max-width: ${sizes[label]}px) {
    ${css.call(undefined, ...args)}
  }

`

A fully typed version that correctly specifies the types for the media.* functions would be :

const sizes = {
    desktop: 992
}

const media = Object.keys(sizes).reduce((acc, label) => {

    acc[label] = (literals: TemplateStringsArray, ...placeholders: any[]) => css`      
    @media(max-width: ${sizes[label]}px) {
        ${css(literals, ...placeholders)}
    }

    `;
    return acc
}, {} as Record<keyof typeof sizes, (l: TemplateStringsArray, ...p: any[]) => string>)
like image 193
Titian Cernicova-Dragomir Avatar answered Jun 06 '26 06:06

Titian Cernicova-Dragomir


The Titian Cernicova-Dragomir solution is great but doesn't work for me. It produces a string with commas. So I add join to css function.

import { css } from "styled-components";

const sizes = {
  desktop: 730,
};

const media = Object.keys(sizes).reduce(
  (acc, label) => {
    acc[label] = (literals: TemplateStringsArray, ...placeholders: any[]) =>
      css`
        @media (max-width: ${sizes[label]}px) {
          ${css(literals, ...placeholders)};
        }
      `.join("");
    return acc;
  },
  {} as Record<
    keyof typeof sizes,
    (l: TemplateStringsArray, ...p: any[]) => string
  >,
);

export default media;
like image 44
Matvii Hodovaniuk Avatar answered Jun 06 '26 07:06

Matvii Hodovaniuk



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!