I want to pass the number of rows (a react native style) dynamically. I tried using 'keyof' as some here have suggested in a different issue, but it didn't work. The code itself works, I'd just like TS to stop screaming at me:
Type '{ backgroundColor: string; flex: number; } | { paddingTop: number | undefined; } | { marginTop: number; fontWeight: "800"; fontSize: number; textAlign: "center"; marginBottom: number; } | { ...; } | ... 7 more ... | { ...; }' is not assignable to type 'StyleProp<ViewStyle>'.
Here's the component:
const Col: React.FC<
PropsWithChildren<{
numRows: number;
}>
> = ({ children, numRows }) => {
return (
<View style={styles[`${numRows}col` as keyof typeof styles]}>{children}</View>
)
}
JSX example:
<Row>
<Col numRows={1}>
<Text>Prompt</Text>
</Col>
<Col numRows={3}>
<Text>This is the prompt</Text>
</Col>
</Row>
Here's the relevant style excerpt:
const styles = StyleSheet.create({
//... other styles
"1col": {
backgroundColor: "lightblue",
borderColor: "#fff",
borderWidth: 1,
flex: 1
},
"2col": {
backgroundColor: "green",
borderColor: "#fff",
borderWidth: 1,
flex: 2
},
"3col": {
backgroundColor: "orange",
borderColor: "#fff",
borderWidth: 1,
flex: 3
},
"4col": {
backgroundColor: "orange",
borderColor: "#fff",
borderWidth: 1,
flex: 4
}
});
The typing is incorrect because you can't accept any number of columns; you should only allow the number of columns you actually support. For example, right now you allow passing 3.14159
to numRows
, but don't have a style for that number.
Typescript may be smart enough to check your style names if you limit numRows
to only what you do support:
const Col: React.FC<
PropsWithChildren<{
numRows: 1 | 2 | 3 | 4;
}>
> = ({ children, numRows }) => {
return (
<View style={styles[`${numRows}col`]}>{children}</View>
)
}
If not, you can select only the styles you have defined:
const Col: React.FC<
PropsWithChildren<{
numRows: 1 | 2 | 3 | 4;
}>
> = ({ children, numRows }) => {
const columnStyle = (() => {
switch (numRows) {
case 1: return styles['1col'];
case 2: return styles['2col'];
// etc
})();
return (
<View style={columnStyle}>{children}</View>
)
}
However, you may not even need these separate styles, due to a misunderstanding of flex
. flex
tells the style how much space to take up relative to the other elements in its container. All you need in your case is flex: 1
.
If you put any number of elements in your row, all with flex: 1
, they will be weighted equally. If you put one element into a row with flex: 4
, it will take up the whole row, because the row total is 4 and the element has 4.
You only need different numbers when you want columns of different widths, for example: 1 item with flex: 2
and two items with flex: 1
in a row will give the first item half the space and the other two a quarter each.