Say I have an object defining my Typography components color and shade.
const theme = {
primary: [10, 20, 50, 100],
secondary: [20, 30, 80],
accent: [10, 20]
}
Typography should accept 2 props, color
and shade
.
Type definition for color can be: keyof typeof theme
.
How do I specify the type of shade
prop such that it only accepts the value within the array of the palette defined in the theme object? For instance, if color is accent, shade cannot be 30. It can only be 10 or 20.
Code snippet:
function Typography({
color,
shade,
children,
}: {
color: keyof typeof theme;
shade: ???;
children: React.ReactNode;
}) {
return <div>{children}</div>;
}
function Main() {
return (
<Typography
color="primary"
shade={}>
Some text
</Typography>
);
}
Codesandbox – https://codesandbox.io/s/conditional-prop-types-cwy49q
2
Answers
You need to use a generic and infer what
shade
could be from that generic:Typescript Playground
There are two possible approaches:
Either of the approaches will require a change in
theme
.Currently, the type of the
theme
isRecord<string, number[]>
which is not suitable for us. We have to prevent the compiler from widening types to their primitives.This can be done by using const assertion:
The problem with const assertion is that we lose type checking and if you use Typescript
>= 4.9
it can be fixed with satisfies operator:Since const assertion converts everything to
readonly
it is crucial to writereadonly number[]
insatisfies
instead of justnumber[]
.Let’s create a type for the
theme
:Approach with generics:
Typography
will accept a generic parameter constrained by thekeyof Theme
and by using it we will get the values forshade
using indexed access:Approach with the union of possible values:
By using mapped types we will map through
Theme
and generate a union of{color: string, shade: number}
:Usage:
Either of these should do the trick, personally, I would recommend the first approach if the
theme
will grow bigger.playground