简体   繁体   中英

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,
})

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;
`;

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.

I've just come across this question while looking for something slightly related. I know it's resolved but I've used styled-components for quite a while and never have happened to see the object syntax you refer to, I presume this is included to allow adoption from other css-in-js options.

However, the reason for me commenting is your flex example, you could achieve a very similar effect with tagged template literals:

const flex = {
  flex: 'display: flex',
  col: 'flex-direction: column'
}

const FlexRow = styled.div`
  ${flex.flex};
`

const FlexCol = styled.div`
  ${flex.flex};
  ${flex.col};
`

Happy styled-componenting 🙂

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM