Search code examples
typescriptreact-nativeexpotypescript-typingsstyled-components

typescript for custom theme styled components


I have tried 2 different ways to do this based on some google searches. I'm trying to setup typing for a custom theme in typescript expo reactive native project. I've setup a declaration ts file and added it to my includes in tsconfig. Here is my setup. Hoping someone out there has went through similar issues and knows how to fix this.

I have a themes folder with the following files that I export out and then import into a index theme file.

themes/
  colors
  sizes
  spacing
  index

Here is the index file importing from the above theme files.

import { DefaultTheme } from "styled-components/native";
import { colors } from "./colors";
import { sizes } from "./sizes";
import { spacing, lineHeights } from "./spacing";

const theme: DefaultTheme = {
  colors,
  sizes,
  spacing,
  lineHeights,
};

export default theme;

then I have my declaration file which I tried 2 ways one manually adding all the props and the other using typeof.

types/theme.d.ts

import {} from "styled-components";
import theme from "../themes";

declare module "styled-components" {
  type Theme = typeof theme;
  export interface DefaultTheme extends Theme {}
}

// Manually adding the props.
// import { DefaultTheme } from "styled-components/native";

// declare module "styled-components" {
//   export interface DefaultTheme {
//     bg: {
//       primary: string;
//       secondary: string;
//     };
//     sizes: stringp[];
//     lineHeights: {
//       title: string;
//       copy: string;
//     };
//     spacing: string[];
//   }
// }

tsconfig.json

{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    "strict": true,
    "baseUrl": ".",
    "paths": {
      "*": ["types/*"]
    },
  },
  "include": ["./src", "./types"],
  "exclude": [
    "node_modules",
    "**/*.test.ts",
    "**/*.test.tsx",
  ]
}

Then this is how I'm using this in my tsx app file.

App.tsx

import React from "react";
import styled, { ThemeProvider } from "styled-components/native";
import { Text, StatusBar } from "react-native";
import theme from "./src/themes";

export default function App() {
  return (
    <ThemeProvider theme={theme}>
        <Container>
          <Text>some text</Text>
          <StatusBar />
        </Container>
    </ThemeProvider>
  );
}

const Container = styled.View`
  flex: 1;
  background-color: ${({ theme }) => theme.colors.bg.primary};
  align-items: center;
  justify-content: center;
`;

Solution

  • Don't set DefaultTheme in your themes/index.ts. It's already used and is just an empty object.

    Update your themes/index.ts to this:

    import { colors } from "./colors";
    import { sizes } from "./sizes";
    import { spacing, lineHeights } from "./spacing";
    
    const theme = {
      colors,
      sizes,
      spacing,
      lineHeights,
    };
    
    export default theme;
    

    Update your types/theme.d.ts this:

    import "styled-components"
    import theme from "./src/themes";
    
    type ThemeInterface = typeof theme
    
    declare module "styled-components" {
        // eslint-disable-next-line @typescript-eslint/no-empty-interface (this is only necessary if you ur eslint complains. Since it should be and Interface and not a Type.) 
        interface DefaultTheme extends ThemeInterface {}
    }
    

    And you should be gucci.