2

So far I have this.

export const PacerThemes = ['Default', 'Dark Muted', 'Neon'] as const;
export type PacerTheme = typeof PacerThemes[number];

type ThemeProperties = {
  exhaleHoldBorderColor: string;
  exhaleHoldColor: string;
  inhaleHoldBorderColor: string;
  inhaleHoldColor: string;
};

type Themes = {
  Default: ThemeProperties;
  'Dark Muted': ThemeProperties;
  Neon: ThemeProperties;
};

Is there a way to generate the Themes type from the PacerThemes array?

4
  • 4
    type Themes = { [ThemeName in PacerTheme]: ThemeProperties; }; typescriptlang.org/docs/handbook/… Commented Feb 17, 2021 at 5:35
  • @GarlefWegart it doesn't work? Commented Feb 17, 2021 at 8:29
  • @GarlefWegart typeof PacerNames[number] also seems to work. I'm really not sure which thing you reference as not working. Commented Feb 17, 2021 at 9:13
  • @VLAZ --- re: "it doesn't work". oh right. my mistake. I thought you made a typo and meant to write PacerTheme_s_ (using PacerThemes as an index is what's not working). [I deleted my comment so that there's no misinformation floating around.] What you suggested is perfectly fine. Commented Feb 17, 2021 at 14:42

2 Answers 2

3

You could use a Mapped type, as suggested in the comments.

    type ThemeNames = "Default" | "Dark Muted" | "Neon";

    type ThemeProperties = {
      exhaleHoldBorderColor: string;
      exhaleHoldColor: string;
      inhaleHoldBorderColor: string;
      inhaleHoldColor: string;
    };

    type Themes = {
      [T in ThemeNames]: ThemeProperties;
    };

Or you could use a Record utility type

    type ThemeNames = "Default" | "Dark Muted" | "Neon";

    type ThemeProperties = {
      exhaleHoldBorderColor: string;
      exhaleHoldColor: string;
      inhaleHoldBorderColor: string;
      inhaleHoldColor: string;
    }

    type Themes = Record<ThemeNames, ThemeProperties>

TS Playground

Sign up to request clarification or add additional context in comments.

3 Comments

Yes. Mapped types are the correct thing to do. However you can solve this without explicitly defining the ThemeNames type. See my answer.
@kasv Thanks for the answer. I like this but it doesn't exactly answer the question. The reason I use an array to define the theme names is to map it into a menu PacerThemes.map((pacerTheme: PacerTheme) =>.
True. However, with this approach you could always use the resulting data structure to map over - Object.keys(myThemes).map(key => {...})
2

Here's a solution (Playground link):

const PacerThemes = ['Default', 'Dark Muted', 'Neon'] as const


type ThemePropertiesVersion = {
  [k in (typeof PacerThemes)[keyof typeof PacerThemes & number]]: "YourType"
}

If you wand to make this more generic by introducing some reusable helper types, you might encounter the problem that readonly any[] does not extend any[].

For example, this would not work, since typeof PacerThemes is readonly:

type Items<A extends any[]> = A[keyof A & number]

To handle this you could to something like this (Playground link):

const PacerThemes = ['Default', 'Dark Muted', 'Neon'] as const


// Version 1 -- allow only readonly arrays

type ReadonlyItems<P extends readonly any[]> = P[keyof P & number]

type TestVersion1 = NonRestrictiveItems<typeof PacerThemes>

type ThemePropertiesVersion1 = {
  [k in ReadonlyItems<typeof PacerThemes>]: "YourType"
}


// Version 2 -- allow both

type NonRestrictiveItems<P extends (readonly any[]) | any[]> = P[keyof P & number]

type TestVersion2 = NonRestrictiveItems<typeof PacerThemes>

type ThemePropertiesVersion2 = {
  [k in NonRestrictiveItems<typeof PacerThemes>]: "YourType"
}

1 Comment

Thanks Garlef. This exactly answered the question. 👍

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.