Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Styled Components TypeScript

Using styled-components, with normal React.js I can do like:

const Container = styled.div({
  userSelect: `none !important`,
})

however with TypeScript I get the error:

Argument of type '{ userSelect: string; }' is not assignable to parameter of type 'TemplateStringsArray'.
  Object literal may only specify known properties, and 'userSelect' does not exist in type 'TemplateStringsArray'.ts(2345)

What's the best way to fix this?

I don't want to use the styled.div template strings approach as I find it a lot less flexible.

For example with template strings we can't do things like:

const flex = {
  flex: display: flex,
  col: flexDirection: `column`
}

const FlexRow = styled.div({
  ...flex.flex,
})

const FlexCol = styled.div({
   ...flex.flex,
   ...flex.col,
})
like image 610
Colin Ricardo Avatar asked Dec 23 '22 22:12

Colin Ricardo


2 Answers

Update: On further investigation, it appears that @Vincent was on the right track before I figured out what was actually going on.

import styled, { CSSObject } from "styled-components";

const Container = styled.div({
  userSelect: "none !important"
} as CSSObject);

Will generate the following error:

Conversion of type '{ userSelect: "none !important"; }' to type 'CSSObject' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
  Type '{ userSelect: "none !important"; }' is not comparable to type 'Properties<string | number>'.
    Types of property 'userSelect' are incompatible.
      Type '"none !important"' is not comparable to type '"contain" | "all" | "-moz-initial" | "inherit" | "initial" | "revert" | "unset" | "auto" | "none" | "text" | "-moz-none" | "element" | undefined'.ts(2352)

So yes, styled components does indeed support this syntax even in TypeScript, it just doesn't understand the !important suffix. Here's a slightly modified solution you might prefer:

const important = <T extends string>(s: T): T => `${s} !important` as T;

const Container = styled.div({
  userSelect: important("none"),
});

It's a little hacky (casting "none !important" as "none" when it clearly isn't), but it keeps your styled CSS props clean and passes the type-checks.


Original Answer: I'm not familiar with that syntax for styled components (it looks a bit like JSS, but not exactly).

I'd recommend using the standard syntax. Styled components are usually written like this:

const Container = styled.div`
  user-select: none !important;
`;
like image 187
p.s.w.g Avatar answered Dec 28 '22 16:12

p.s.w.g


it doesn't recognize the !important so just cast it to any to quiet typescript.

styled.div({
  userSelect: 'none !important'  as any
});

EDIT - explanation of why this works

its very simple. if you use an ide like atom you can "go to" the type for the userSelect property. The type is UserSelectProperty and its value must be one of these exactly.

export type Globals = "-moz-initial" | "inherit" | "initial" | "revert" | "unset";
export type UserSelectProperty = Globals | "-moz-none" | "all" | "auto" | "contain" | "element" | "none" | "text";

Since none !important is not an option, you have to cast it to any.

like image 33
Vincent Avatar answered Dec 28 '22 17:12

Vincent