Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type safety in theme object #17

Closed
dested opened this issue Jun 26, 2020 · 2 comments
Closed

Type safety in theme object #17

dested opened this issue Jun 26, 2020 · 2 comments

Comments

@dested
Copy link

dested commented Jun 26, 2020

Hey everyone, Great project!

As I began implementing I was getting frustrated that the types in my theme object were still a bit loose. I wouldn't know until runtime that I mistyped a color or a border radius key. To remedy this I created a couple of helper functions:

type BaseTheme<
  TColors extends {[key: string]: string},
  TSpacing extends {[key: string]: number},
  TBreakpoints extends {[key: string]: number},
  TZIndices extends {[key: string]: number},
  TBorderRadii extends {[key: string]: number}
> = {
  colors: TColors;
  spacing: TSpacing;
  breakpoints: TBreakpoints;
  zIndices?: TZIndices;
  borderRadii?: TBorderRadii;
};

function makeBaseTheme<
  TColors extends {[key: string]: string},
  TSpacing extends {[key: string]: number},
  TBreakpoints extends {[key: string]: number},
  TZIndices extends {[key: string]: number},
  TBorderRadii extends {[key: string]: number}
>(
  t: BaseTheme<TColors, TSpacing, TBreakpoints, TZIndices, TBorderRadii>
): BaseTheme<TColors, TSpacing, TBreakpoints, TZIndices, TBorderRadii> {
  return t;
}

function makeTheme<
  TColorKeys extends string,
  TColors extends {[key: string]: string},
  TSpacing extends {[key: string]: number},
  TBreakpoints extends {[key: string]: number},
  TZIndices extends {[key: string]: number},
  TBorderRadii extends {[key: string]: number},
  BTheme extends BaseTheme<TColors, TSpacing, TBreakpoints, TZIndices, TBorderRadii>,
  T extends {
    [topVariant: string]: {
      [variant: string]: AllProps<BTheme>;
    };
  }
>(
  base: BaseTheme<TColors, TSpacing, TBreakpoints, TZIndices, TBorderRadii>,
  t: T
): BaseTheme<TColors, TSpacing, TBreakpoints, TZIndices, TBorderRadii> & T {
  return {...base, ...t};
}

Used as:

const baseTheme = makeBaseTheme({
  colors: {
    primary: '#F4F4F7',
    primaryDark: '#C4C4C7',
  },
  spacing: {
    s: 8,
    m: 16,
    l: 24,
    xl: 40,
  },
  zIndices: {
    behind: -1,
  },
  breakpoints: {
    phone: 0,
    tablet: 768,
  },
  borderRadii: {
    button: 5,
  },
});


export const theme = makeTheme(baseTheme, {
  buttonVariants: {
    simpleButton: {
      borderRadius: 'button',
      backgroundColor: 'primary',
      flexDirection: 'row',
      padding: 's',
    },
  },
});
export type Theme = typeof theme;

This enforces type safety on the variants appropriately!

If this is an interesting enough concept to the maintainers I can submit a PR and expose these two functions for common use.

@JoelBesada
Copy link
Contributor

Hi there, thanks for posting this suggestion! I'm currently on vacation until July 13, but I'll see if I can get someone else on the team to read through and share their thoughts on this before that.

@JoelBesada
Copy link
Contributor

Fixed with #18

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants