'Is there are way to optimize typescript theme generation?

I'm developing React/React Native theme provider and want to have ability to specify spacing style properties base on sizes I define in theme. My current type of theme spacing looks like:

export interface Sizes {
    auto: "auto";
    xxs: number;
    xs: number;
    s: number;
    m: number;
    l: number;
    xl: number;
    xxl: number;
    full: "100%";
}

export type SpacingDirection = "base" | "top" | "right" | "left" | "bottom" | "horizontal" | "vertical";

export type MarginVariant<
    D extends SpacingDirection,
    P extends "margin" | "marginTop" | "marginRight" | "marginLeft" | "marginBottom" | "marginHorizontal" | "marginVertical"
> = Record<D, Record<keyof Sizes, Record<P, Sizes[keyof Sizes]>>>;

export type Margin = MarginVariant<"base", "margin"> &
    MarginVariant<"top", "marginTop"> &
    MarginVariant<"right", "marginRight"> &
    MarginVariant<"left", "marginLeft"> &
    MarginVariant<"bottom", "marginBottom"> &
    MarginVariant<"horizontal", "marginHorizontal"> &
    MarginVariant<"vertical", "marginVertical">;

export type PaddingVariant<
    D extends SpacingDirection,
    P extends "padding" | "paddingTop" | "paddingRight" | "paddingLeft" | "paddingBottom" | "paddingHorizontal" | "paddingVertical"
> = Record<D, Record<keyof Sizes, Record<P, Sizes[keyof Sizes]>>>;

export type Padding = PaddingVariant<"base", "padding"> &
    PaddingVariant<"top", "paddingTop"> &
    PaddingVariant<"right", "paddingRight"> &
    PaddingVariant<"left", "paddingLeft"> &
    PaddingVariant<"bottom", "paddingBottom"> &
    PaddingVariant<"horizontal", "paddingHorizontal"> &
    PaddingVariant<"vertical", "paddingVertical">;

And actual theme implementing spacing interface looks like:

const sizes: Sizes = {
    auto: "auto",
    xxs: 2,
    xs: 4,
    s: 8,
    m: 12,
    l: 16,
    xl: 24,
    xxl: 32,
    full: "100%",
};

const spacings: Spacings = {
    margin: {
        base: {
            auto: { margin: sizes.auto },
            xxs: { margin: sizes.xxs },
            xs: { margin: sizes.xs },
            s: { margin: sizes.s },
            m: { margin: sizes.m },
            l: { margin: sizes.l },
            xl: { margin: sizes.xl },
            xxl: { margin: sizes.xxl },
            full: { margin: sizes.full },
        },
        top: {
            auto: { marginTop: sizes.auto },
            xxs: { marginTop: sizes.xxs },
            xs: { marginTop: sizes.xs },
            s: { marginTop: sizes.s },
            m: { marginTop: sizes.m },
            l: { marginTop: sizes.l },
            xl: { marginTop: sizes.xl },
            xxl: { marginTop: sizes.xxl },
            full: { marginTop: sizes.full },
        },
        bottom: {
            auto: { marginBottom: sizes.auto },
            xxs: { marginBottom: sizes.xxs },
            xs: { marginBottom: sizes.xs },
            s: { marginBottom: sizes.s },
            m: { marginBottom: sizes.m },
            l: { marginBottom: sizes.l },
            xl: { marginBottom: sizes.xl },
            xxl: { marginBottom: sizes.xxl },
            full: { marginBottom: sizes.full },
        },
        left: {
            auto: { marginLeft: sizes.auto },
            xxs: { marginLeft: sizes.xxs },
            xs: { marginLeft: sizes.xs },
            s: { marginLeft: sizes.s },
            m: { marginLeft: sizes.m },
            l: { marginLeft: sizes.l },
            xl: { marginLeft: sizes.xl },
            xxl: { marginLeft: sizes.xxl },
            full: { marginLeft: sizes.full },
        },
        right: {
            auto: { marginRight: sizes.auto },
            xxs: { marginRight: sizes.xxs },
            xs: { marginRight: sizes.xs },
            s: { marginRight: sizes.s },
            m: { marginRight: sizes.m },
            l: { marginRight: sizes.l },
            xl: { marginRight: sizes.xl },
            xxl: { marginRight: sizes.xxl },
            full: { marginRight: sizes.full },
        },
        vertical: {
            auto: { marginVertical: sizes.auto },
            xxs: { marginVertical: sizes.xxs },
            xs: { marginVertical: sizes.xs },
            s: { marginVertical: sizes.s },
            m: { marginVertical: sizes.m },
            l: { marginVertical: sizes.l },
            xl: { marginVertical: sizes.xl },
            xxl: { marginVertical: sizes.xxl },
            full: { marginVertical: sizes.full },
        },
        horizontal: {
            auto: { marginHorizontal: sizes.auto },
            xxs: { marginHorizontal: sizes.xxs },
            xs: { marginHorizontal: sizes.xs },
            s: { marginHorizontal: sizes.s },
            m: { marginHorizontal: sizes.m },
            l: { marginHorizontal: sizes.l },
            xl: { marginHorizontal: sizes.xl },
            xxl: { marginHorizontal: sizes.xxl },
            full: { marginHorizontal: sizes.full },
        },
    },
    padding: {
        base: {
            auto: { padding: sizes.auto },
            xxs: { padding: sizes.xxs },
            xs: { padding: sizes.xs },
            s: { padding: sizes.s },
            m: { padding: sizes.m },
            l: { padding: sizes.l },
            xl: { padding: sizes.xl },
            xxl: { padding: sizes.xxl },
            full: { padding: sizes.full },
        },
        top: {
            auto: { paddingTop: sizes.auto },
            xxs: { paddingTop: sizes.xxs },
            xs: { paddingTop: sizes.xs },
            s: { paddingTop: sizes.s },
            m: { paddingTop: sizes.m },
            l: { paddingTop: sizes.l },
            xl: { paddingTop: sizes.xl },
            xxl: { paddingTop: sizes.xxl },
            full: { paddingTop: sizes.full },
        },
        bottom: {
            auto: { paddingBottom: sizes.auto },
            xxs: { paddingBottom: sizes.xxs },
            xs: { paddingBottom: sizes.xs },
            s: { paddingBottom: sizes.s },
            m: { paddingBottom: sizes.m },
            l: { paddingBottom: sizes.l },
            xl: { paddingBottom: sizes.xl },
            xxl: { paddingBottom: sizes.xxl },
            full: { paddingBottom: sizes.full },
        },
        left: {
            auto: { paddingLeft: sizes.auto },
            xxs: { paddingLeft: sizes.xxs },
            xs: { paddingLeft: sizes.xs },
            s: { paddingLeft: sizes.s },
            m: { paddingLeft: sizes.m },
            l: { paddingLeft: sizes.l },
            xl: { paddingLeft: sizes.xl },
            xxl: { paddingLeft: sizes.xxl },
            full: { paddingLeft: sizes.full },
        },
        right: {
            auto: { paddingRight: sizes.auto },
            xxs: { paddingRight: sizes.xxs },
            xs: { paddingRight: sizes.xs },
            s: { paddingRight: sizes.s },
            m: { paddingRight: sizes.m },
            l: { paddingRight: sizes.l },
            xl: { paddingRight: sizes.xl },
            xxl: { paddingRight: sizes.xxl },
            full: { paddingRight: sizes.full },
        },
        vertical: {
            auto: { paddingVertical: sizes.auto },
            xxs: { paddingVertical: sizes.xxs },
            xs: { paddingVertical: sizes.xs },
            s: { paddingVertical: sizes.s },
            m: { paddingVertical: sizes.m },
            l: { paddingVertical: sizes.l },
            xl: { paddingVertical: sizes.xl },
            xxl: { paddingVertical: sizes.xxl },
            full: { paddingVertical: sizes.full },
        },
        horizontal: {
            auto: { paddingHorizontal: sizes.auto },
            xxs: { paddingHorizontal: sizes.xxs },
            xs: { paddingHorizontal: sizes.xs },
            s: { paddingHorizontal: sizes.s },
            m: { paddingHorizontal: sizes.m },
            l: { paddingHorizontal: sizes.l },
            xl: { paddingHorizontal: sizes.xl },
            xxl: { paddingHorizontal: sizes.xxl },
            full: { paddingHorizontal: sizes.full },
        },
    },
};

And will be use like that:

<View style={theme.spacings.margin.bottom.xxl} />

It looks like not DRY code and maybe there is a way to optimize this code to more readable and maintanable code?



Solution 1:[1]

you can try to use a reducer maybe, I made something quick to show you an example, but you can enhance it further using the same logic

const sizesReducer = (value) => {
    const object = Object.keys(sizes).reduce(
        (accumulator, key) => (
            (accumulator[key] = { [`${value}`]: sizes[key] }), accumulator
        ),
        {}
    );
    return object;
};

const spacings = {
    margin:{
        base: sizesReducer("margin"),
        top: sizesReducer("marginTop"),
        bottom: sizesReducer("marginBottom"),
        left: sizesReducer("marginLeft"),
        right: sizesReducer("marginRight"),
        vertical: sizesReducer("marginVertical"),
        horizontal: sizesReducer("marginHorizontal"),
    },
    padding: {
        base: sizesReducer("padding"),
        top: sizesReducer("paddingTop"),
        bottom: sizesReducer("paddingBottom"),
        left: sizesReducer("paddingLeft"),
        right: sizesReducer("paddingRight"),
        vertical: sizesReducer("paddingVertical"),
        horizontal: sizesReducer("paddingHorizontal"),
    },
};

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 RodSar