I’m using a select list in React with TypeScript.
const allArtists = ["elvis", "dr dre"] as const;
type Artist = (typeof allArtists)[number];
function App() {
const [artist, setArtist] = useState<Artist | "">("");
function handleArtistChange(e: React.ChangeEvent<HTMLSelectElement>) {
const newArtist = e.target.value as Artist | "";
setArtist(newArtist);
}
return (
<div>
<select value={artist} onChange={handleArtistChange}>
<option value={""}>Please choose</option>
{allArtists.map((a) => (
<option key={a} value={a}>
{a}
</option>
))}
</select>
</div>
);
}
The code above isn’t great as if an illegal value is added as an option I don’t get TypeScript errors:
<option value={"ILLEGAL VALUE"}>Please choose</option>
I know one option would be to check if the string passed to handleArtistChange
appears in allArtists
. This also isn’t ideal as there is a runtime performance, and "ILLEGAL VALUE"
still doesn’t produce a compile time error.
Is there a compile time solution that doesn’t use type casting?
2
Answers
With some contortions and a couple helper components, at least:
To do this, you need to create a custom option component to do this.
You need to use the
ArtistOption
component in place ofoption
tag in your code.You can even generalize this with generic types. However, I find it more useful to keep it simple unless you are creating something reusable for multiple use cases.