If you just declare MenuOptions
as above, the compiler will tend to infer the value types as just string[]
instead of a tuple of literals. If you need access to those literals then you will need to change how you declare your object. When TypeScript 3.4 comes out in March 2019 you will be able to use a const context to hint the compiler to infer the narrowest possible type, like this:
const MenuOptions = {
Project: ["new", "open", "close"],
File: ["create", "edit"]
// ...
} as const; // only works in TS3.4+
Until then you can use a helper function like the following to guide the inference:
type Narrowable = string | number | boolean | undefined | null | void | {};
const tuple = <T extends Narrowable[]>(...t: T)=> t;
const MenuOptions = {
Project: tuple("new", "open", "close"),
File: tuple("create", "edit")
// ...
};
In either case the type you want is given above as
type MenuOption = (typeof MenuOptions)[keyof typeof MenuOptions][number];
Let's break that apart into steps and give names to the intermediate types. You are taking the type of MenuOptions
:
type TypeofMenuOptions = typeof MenuOptions;
// type TypeofMenuOptions = {
// Project: ["new", "open", "close"];
// File: ["create", "edit"];
// }
looking up a union of its value types
type MenuOptionsValues = TypeofMenuOptions[keyof TypeofMenuOptions];
// type MenuOptionsValues = ["new", "open", "close"] | ["create", "edit"]
and looking up its numerically-indexed properties (If you have a type (A | B)[K]
it is evaluated as A[K] | B[K]
)
type MenuOption = MenuOptionsValues[number];
// type MenuOption = "new" | "open" | "close" | "create" | "edit"
Which leaves you with the union of string literals you were looking for.
Okay, hope that helps. Good luck!