'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 |
