Generate Type from Dictionary

Generate Type from Dictionary

Necessity is the Mother of Invention

If you read my New Year's Post, then you know that I am not a big AI gal. In fact, I can count on my pinky finger the number of times I used generative AI last year. But I am trying to be more open-minded this year.

Thus, instead of googling, I turned to ChatGPT when I ran into a coding problem in a recent project. I am sharing my experience here because: a) it has reinforced the idea that AI can be a tool for learning (not just quick fixes), and b) the approach is cool.

Note: this is a "challenge" post, meaning that it includes a problem that we challenge you to solve. Some (many?) will be able to solve the problem from your vast Typescript experience. But, if you are like me and are stumped on how to proceed, try AI and see if you can as it as a tutor where you come away with both an answer and a better understanding of how TS works.

Background

Before describing the problem, let me share how we define string literal union types in our applications. Traditionally, one just lists the possible values...

typescript
1type Color = 'red' | 'green' | 'blue';

We use a different approach (most of the time)...

typescript
1const colorList = ['red', 'green', 'blue'] as const;
2
3type Color = typeof colorList[number];

We include a list of the possible literals (colorList) because this gives us an array of possible values, so we can create validation methods like this.

typescript
1function isColor(value: unknown): value is Color {
2  return typeof(colorList.find(m => m === value)) === 'string';
3}

We extrapolate this to create generic validators and parsers.

typescript
1function isStringUnionType<T extends string>(
2  value: unknown, possible: Readonly<T[]>): value is T {
3  return typeof(possible.find(m => m === value)) === 'string';
4}
5
6function toStringUnionType<T extends string>(
7  value: unknown, possible: Readonly<T[]>): T | null {
8  return isStringUnionType<T>(value, possible) 
9    ? value 
10    : null;
11}

Our approach of basing string literal union types on a readonly array of literals creates a single source of truth for the possible values. If we want to add a color, we just add it to colorList, and our type, validators, parsers, and other places in our code, like dropdown lists, are updated as well.

The Challenge

We have a list of favorite CSS named-colors from the four designers in our application.

typescript
1const responseDictionary = {
2  roger: ['green', 'maroon', 'navy'],
3  leon: ['teal', 'olive', 'silver'],
4  tori: ['fushsia', 'yellow'],
5  sara: ['azure', 'coral', 'crimson', 'gold'],
6};

We want to create three types from this SSoT:

typescript
1type Designer = ...; //string literal union of the designers' names
2
3type Color = ...;    //string literal union of all colors from the dictionary
4
5type ColorSuggestion = ...;  //dictionary relating a designer with a color.

The ColorSuggestion dictionary can include any number of designers (0 to all), but each designer can provide only one color suggestion.

Pause now if you would like to work on the challenge. We discuss the solution below.

"My" Solution

I quickly coded the first type definition Designer, as it is just the keyof the typeof resonseDictionary.

typescript
1type Designer = keyof typeof responseDictionary;

But the second type caused me to pause. Check out this playground to get an idea of the direction I was heading, or just for a good laugh.

After some thinking, I came up wthi this...

typescript
1//second attempt
2const colors = Object.values(responseDictionary).flat();
3type Color = typeof colors[number];
4//      ^? type Color = string

As you can see, this kinda works. I extracted the values (colors) from the dictionary and then used our pattern of typeof array[number] to pull the type of the individual elements of the array. You can see that this result is a string type alias, not a literal union type.

I have seen this error often, and know how to resolve it... the original responseDictionary needs to be marked as a const.

typescript
1const responseDictionary = {
2    roger: ['green', 'maroon', 'navy'],
3    leon: ['teal', 'olive', 'silver'],
4    tori: ['fushsia', 'yellow'],    
5    sara: ['azure', 'coral', 'crimson', 'gold'],
6} as const;  //important - mark as const
7
8//third attempt
9const colors = Object.values(responseDictionary).flat();
10type Color = typeof colors[number];
11//      ^? type Color = "green" | "maroon" | "navy" | ...

Done! ... But this approach requires me to create the color array (colors) which seems kind of wasteful since these values are already in the variable responseDictionary. How can I create the Color type without creating a new variable?

In keeping with my goal of exploring AI this year, I turned to Claude to help me with the solution and to understand that solution. (link to the prompt and Claude's response)

typescript
1//Claude's attempt
2type Color = typeof responseDictionary[Designer][number];
3//      ^? type Color = "green" | "maroon" | "navy" | ...
4
5// important - responseDictionary is marked `as const`

The important part of my chat was the discussion of why the solution works. It resulted in an aha moment. I have been using this pattern for years, but never thought to expand it to multiple levels. Again, check out Claud's explanation to learn more about why this approach works.

Finally, I can add the ColorSuggestion typing...

typescript
1type ColorSuggestion = {
2    [key in Designer]?: Color
3};

Extra Credit

In working with the two literal union types (Designer and Color), we will need to have arrays of the possible values so we can pass them into our isStringUnionType type guard function and toStringUnionType parser.

The simplest way is to cast the keys and values...

typescript
1const designerList = Object.keys(responseDictionary) as Designer[];
2//      ^? const designerList = ("roger" | "leon" | ...)[]
3const colorList = Object.values(responseDictionary).flat() as Color[];
4//      ^? const colorList = ("green" | "maroon" | ...)[]

You can see my work in [this playground].

Takeaway

The takeaway is that you can, and really should, include an ask for an explanation whenever turning to AI to produce some code for you. Not only will it help you learn, but you might also even catch an error in the code produced or an error in the requirements you provided. AI is not infallible. So don't just accept the code without really understanding it (and, of course, testing it).

popularity
Electric
posted
Feb. 09, 2026