Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Better typed function to generate media queries

I am following along with this post: https://medium.com/@samuelresua/easy-media-queries-in-styled-components-690b78f50053

I have created the following in Typescript, but have had to resort to typing to any more than I have to I'm sure:

const breakpoints: ObjectMap<number> = {
  small: 767,
  medium: 992,
  large: 1200,
  extraLarge: 1240
};

export const media = Object.keys(breakpoints).reduce((acc: { [key: string]: (...args: any) => any }, label) => {
  acc[label] = (...args) => css`
     @media (min-width: ${breakpoints[label]}px) {
        ${css(...args as [any])};
     }
  `;
  return acc;
}, {});

As a result, I have no help in my IDE when I'm writing styles in my media query blocks:

styled.button<Props>`
  background: red; /* this must be a valid style */
  ${({ theme }) => theme.media.large`
      background: blue;
      foo: bar; /* this isn't caught */
   `

Anyone know how I can improve my media function?

like image 857
Mister Epic Avatar asked Mar 27 '20 15:03

Mister Epic


2 Answers

I believe the isn’t a TypeScript issue but rather an IntelliSense plugin issue.

From the perspective of TypeScript, the content between the backticks is of type TemplateStringsArray. TypeScript isn’t aware of valid CSS properties as it sees an array of strings.

I believe the validation is done at the plugin level by typescript-styled-plugin.

See https://github.com/styled-components/vscode-styled-components/pull/41

I would submit a feature request or bug report here.

like image 73
sunknudsen Avatar answered Oct 23 '22 14:10

sunknudsen


If I understand correctly, you want to get rid of any.

Here's how you can do it:

import {
  css,
  CSSObject,
  SimpleInterpolation,
  FlattenSimpleInterpolation,
} from 'styled-components';

type ObjectMap<T> = {[key: string]: T};

const breakpoints: ObjectMap<number> = {
  small: 767,
  medium: 992,
  large: 1200,
  extraLarge: 1240,
};

export const media = Object.keys(breakpoints).reduce(
  (
    acc: {
      [key: string]: (
        first: TemplateStringsArray | CSSObject,
        ...interpolations: SimpleInterpolation[]
      ) => FlattenSimpleInterpolation;
      // ^^^ this is the first implementation of  `BaseThemedCssFunction`
    },
    label
  ) => {
    acc[label] = (...args) => css`
      @media (min-width: ${breakpoints[label]}px) {
        ${css(...args)};
      }
    `;
    return acc;
  },
  {}
);
like image 2
Maxim Mazurok Avatar answered Oct 23 '22 15:10

Maxim Mazurok