Search code examples
javascripttypescriptreact-nativereact-native-elements

declaration merging for react-native-elements theme


I am using react-native-elements in my react-native application.

My app is wrapped with the ThemeProvider to pass the theme down to all components.

<SafeAreaProvider>
 <ThemeProvider theme={Theme}>
    <Loader visible={loader.loading} text={loader.message} absolute={true} />
    <RootNavigation />
  </ThemeProvider>
</SafeAreaProvider>

In the theme file i define the values i want to use across the app.

const theme = {
  colors: {
    primary: '#6A69E2',
    primaryDark: '#4747c2',
    primaryLight: 'rgba(106, 105, 226, 0.35)',
    gray: {
      dark: '#242424',
      default: '#666',
      medium: '#999',
      light: '#ccc',
      lightest: '#e7e7e7',
    },
  },
  text: {
    size: {
      small: 12,
      default: 16,
      large: 18,
      h1: 26,
      h2: 22,
      h3: 20,
    },
  },
  Text: {
    style: {
      fontSize: 16,
      color: '#242424',
      fontFamily: 'Roboto',
    },
  },
  Button: {
    style: {
      borderRadius: 50,
    },
    disabledStyle: {
      backgroundColor: 'rgba(106, 105, 226, 0.35)',
    },
  },
};

export default theme;

For the values the original theme of react-native-elements providing this is working. For example i can access the colors by using

const theme = useTheme()
theme.colors.primary

But when i want to add some new properties like primaryDark i'll get an linter error.

Object literal may only specify known properties, and 'primaryDark' does not exist in type 'RecursivePartial<Colors>'.ts(2322)

In the doc of react-native-elements is a part about declaration merging, but i don't understand how i can archive this https://reactnativeelements.com/docs/customization/#typescript-definitions-extending-the-default-theme.

Somebody could help me with this?


Solution

  • Well, declaration merging still works. This seems like a bug on the lib's part.

    Their doc says you can augment the Color interface in module 'react-native-elements'. But currently (as of 2021-04-18, with v3.3.2) that interface is actually hidden inside module 'react-native-elements/dist/config/colors', not directly exposed at the top level, weird.

    I suggest you file an issue to their repo. Never mind, someone already filed the issue.

    Tested on my machine, following solution works.

    import React from 'react'
    import { useTheme, ThemeProvider } from 'react-native-elements'
    
    declare module 'react-native-elements/dist/config/colors' {
      export interface Colors {
        primaryDark: string
        primaryLight: string
      }
    }
    
    const ChildComp = () => {
      const theme = useTheme()
      theme.theme.colors.primaryDark // <-- No more error 🎉
      return <div>foobar</div>
    }
    

    Reply to OP's comment. You can augment interface however you like, as long as the augmented key doesn't exist before. For example add foobar key to FullTheme.

    declare module 'react-native-elements' {
      export interface FullTheme {
        foobar: string
      }
    }