I'm using MaterialUI's sx
property to style some react components. I find having the full style definition inline with the components leads to messy, screen filling component bodies, so I have moved all the style definitions into a constant object outside the function component. Here's a quick example:
const styles = {
a: {/*...*/}
b: {/*...*/}
}
const component = () => <Box sx={styles.a}>
<Button sx={styles.b}>Button Text</Button>
</Box>
The problem I'm having is ensuring that styles.a and styles.b are valid objects, which is difficult without the IDE knowing what types they should be. If I declare styles: Record<string, SystemStyleObject>
I gain type checking when defining a and b, but I lose knowledge over the actual keys of styles
and accessing styles.c
no longer errors.
I've come up with two clunky work-arounds:
const a: SystemStyleObject = {}
const b: SystemStyleObject = {}
const styles = {a, b}
This gets me to the end product I want, but it requires declaring every single style in a separate variable, as well as explicity typing them.
const styles: Record<'a' | 'b', SystemStyleObject> = {
a: {/*...*/}
b: {/*...*/}
}
This also gets me to the same end product, but makes adding or changing the keys of the object awkward as you have to add them to the union as well as the body.
#2 is the one I've gone with for now, but I'm wondering if there's a better way to achieve this.
This is now possible in ts 4.9 with the satisfies
keyword. This validates the type but does not corece the result into the wider type.
interface thing {
prop: string;
}
const stuff = {
this: {
prop: "hello",
},
that: {
// Errors as prop is not defined
},
something: {
prop: "test",
otherProp: 4,
// Errors as otherProp does not exist in the interface.
}
} satisfies Record<string, thing>
console.log(stuff.this.prop);
console.log(stuff.what.prop);
// Errors as stuff.what is not defined
The final type of stuff
is not Record<string, thing>
it is
{
this: {
prop: string;
}
that: {}
something: {
prop: string;
otherProp: number;
}
}