Pluto! Once a member of our own solar system's list of 9 planets, is now considered a dwarf planet. Who thought that list would change? And if Pluto is ever re-considered to be more than a dwarf, what happens to Planet Nine's name?
Similar to our solar system, we sometimes expect that some code or master data doesn't change. But when it does,
-
["Pluto"]
is often hardcoded in arrays for dropdown-lists etc. -
{"Pluto": {"discovered":1930, "namedAfter": "Pluto"}}
can be found in keys or values of complex json objects - or it is a parameter of a
function getLegendFromMythologyByDeity(f: "Pluto" | "Flora" | "Juno" | "Apollo" | "Minerva" | "Diana" | "Vulcan" | "Sol")
.
So to improve the situation, we can fill these types by hand. They're called string literal types, more on that topic here:
export type PlanetsType: "Mercury" | "Venus" | "Earth" | "Mars" | "Jupiter" | "Saturn" | "Uranus"
| "Neptune";
//| "Pluto";
export type MythologicalDeityType: "Pluto" | "Flora" | "Juno" | "Apollo" | "Minerva" | "Diana" | "Vulcan" | "Sol";
export type MickeysFriendsType: "Pluto" | "Minnie" | "Donald";
If you additionally want an array that contains all possible permutations of a string literal union type, you could fill that by hand:
export const PlanetsTypeKeys: PlanetsType[] = [
"Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus"
, "Neptune"];
...but you would have to remember to update that each time a new string literal type enters that union.
export type PlanetsType: "Mercury" | "Venus" | "Earth" | "Mars" | "Jupiter" | "Saturn" | "Uranus"
| "Neptune"
| "Planet9TheMisterious"; // <-- the new one, now the array has to be updated
A self-sustaining solution
Remembering to update the "right part" of the code is usually hard, even if you leave a comment. Throwing type errors is thus much better - and because we can't create an Array (runtime - javascript) from a union type (compile time - typescript), we'll use a helper, that'll finally bring us back to the master data.
/**
* PlanetsType is a union type of string literals. This is useful for making switch-case statements exhaustive:
* https://www.typescriptlang.org/docs/handbook/2/narrowing.html#exhaustiveness-checking
*
* below however, we want to get an exhaustive list of all string literals in the union type. By building a helper
* object with the key set to "s in PlanetsType", we effectively create an enum-like structure with all the
* string literals of PlanetsType. When you create or change PlanetsType, this helps to keep the
* array in PlanetsTypeKeys up-to-date by throwing a typing error. The order of the entries is also preserved
*/
const PlanetsTypeHelperObj: { [s in PlanetsType ]: PlanetsType } = {
"Mercury":"Mercury",
"Venus":"Venus",
"Earth":"Earth",
"Mars":"Mars",
"Jupiter":"Jupiter",
"Saturn":"Saturn",
"Uranus":"Uranus",
"Neptune":"Neptune",
//"Pluto":"Pluto",
"Planet9TheMisterious":"Planet9TheMisterious",
}
export const PlanetsTypeKeys: PlanetsType[] = Object.keys(PlanetsTypeHelperObj) as PlanetsType[]
This turns PlanetsTypeKeys
into an exhaustive Array of string literals in typescript. Because it's based on a json object (also called "dictionary" in this usage), there are no duplicates. Finally, the value-side of the json object ({"VenusKey":"VenusValue"}
) can be anything you like. If you'd like to make the value-part your master data, the PlanetsTypeKeys
will stay up to date, ready to be used ... with a dropdown for example.
Top comments (0)