DEV Community

loading...
Cover image for Create styled.d.ts to make Typescript work with styled-components

Create styled.d.ts to make Typescript work with styled-components

rajuashok profile image Ashok Raju ・3 min read

TL;DR

Create theme.ts

I put mine in /theme/theme.ts. This is what it looks like:

export type ThemeType = typeof light; // This is the type definition for my theme object.

export const light = {
  primary: "#f45511",
  text: "#000",
  background: "#fff",
  ...
}
export const dark: ThemeType = {
  primary: ...
  ...
}

const theme = light; // set the light theme as the default.
export default theme;

Create styled.d.ts

I put mine in /theme/styled.d.ts. This is what it looks like:

import {} from 'styled-components';
import { ThemeType } from './theme'; // Import type from above file
declare module 'styled-components' {
  export interface DefaultTheme extends ThemeType {} // extends the global DefaultTheme with our ThemeType.
}
  • import { ThemeType } from './theme'; -> This line imports my unique theme type definition from the above theme.ts file.
  • export interface DefaultTheme extends ThemeType {} -> This line extends the global DefaultTheme to now include properties from my ThemeType.

Now props.theme.primary will auto-complete and typescript will not have a compile error.

The Long Version

A common pattern in styled-components is to create a top-level theme object (just a plain old object) and have it be passed down to all components using ThemeProvider. Here's an example:

import { ThemeProvider } from 'styled-components';
import theme from './theme';

const App = () => (
  {/* theme obj being passed down through ThemeProvider */}
  <ThemeProvider theme={theme}>
    <Button>Click Me</Button>
  </ThemeProvider>
)

// button.tsx
const Button = styled.button`
  color: ${props => (
    props.theme.primary
    /* accessing props.theme */
  )};
  background-color: ${props => props.theme.background};
`;

As you can see, ThemeProvider passes the theme object to all child components. In the above case, the Button component uses this theme object to set its color and background.

Check out the styled-components docs for more details on theme support.

This is all great, but when we use Typescript, it will give us an error since it doesn't know primary is a field inside theme. Like this:

Screen Shot 2020-09-18 at 3.32.57 PM

Typescript is complaining because the default theme object is just {}. The ThemeProvider injects an object of type DefaultTheme into every prop and the definition for DefaultTheme is just {}. Take a look at the type definition.

To solve this we need to use declaration merging and extend DefaultTheme. I've found the best way to do this is:

Create theme.ts

Create theme.ts. I usually create this in /theme/theme.ts. This is an example of what it could look like:

export type ThemeType = typeof light; // This is the type definition for my theme object.

export const light = {
  primary: "#f45511",
  text: "#000",
  background: "#fff",
  ...
}
export const dark: ThemeType = {
  primary: ...
  ...
}

const theme = light; // set the light theme as the default.
export default theme;

This creates the theme, but also exports a ThemeType to be used by styled.d.ts below.

Create styled.d.ts

Create a styled.d.ts file in your project anywhere (most likely your bundler will pick it up. If not, that's something you'll have to look into).

Add this code to extend the DefaultTheme and merge it with ThemeType from your theme.ts file.

import {} from 'styled-components';
import { ThemeType } from './theme'; // Import type from above file
declare module 'styled-components' {
  export interface DefaultTheme extends ThemeType {} // extends the global DefaultTheme with our ThemeType.
}

That's it! You're typescript compiler and VSCode should stop complaining about props.theme.primary.

If you enjoyed this post... I have nothing to ask of you. Cheers.

Discussion

pic
Editor guide