I am trying to find a way to enforce a type of a prop by using the values of another prop.
The list of array options could be anything set when the component is used, so I can't manually type the options. Is this even possible?
// Props
export interface Props {
options: string[]; // These will be the options
selected: string; // Enforce this to be one the options
}
// Component
const Component = ({ options, selected }: Props) => {
...
return <div>{selected}</div>;
};
// Usage of component somewhere else
<Component
options={['2020', '2021', '2022', '2023']}
selected="1999" // <--- This should return an error
/>
I did attempt to use generics but I am fairly new to Typescript and failed :(
CodePudding user response:
- Define
options
as a tuple using the spread operator. This allows to use the strings array as a list of literal types. - Use
number
to index the array and create a constraint for theselected
prop (a union of all the array values).
// Props
export interface Props<T extends string[]> {
options: [...T]; // These will be the options
selected: T[number]; // Enforce this to be one the options
}
// Component
const Component = <T extends string[]>(props: Props<T>) => {
return <div>{props.selected}</div>;
};
// Usage of component somewhere else
<Component
options={['2020', '2021', '2022', '2023']}
selected="1999"
></Component>
CodePudding user response:
Conceptually you need Props
to be generic in the union of string literal types of the options
array elements:
interface Props<T extends string> {
options: T[];
selected: T
}
A wrinkle is that in your component, the compiler will happily infer T
from both the options
and the selected
properties, preventing your desired error from happening:
const Component = <T extends string>({ options, selected }: Props<T>) => {
return <div>{selected}</div>;
};
<Component
options={['2020', '2021', '2022', '2023']}
selected="2019" // no error
></Component>
// Props<"2020" | "2021" | "2022" | "2023" | "2019"> inferred