#Flatten nested object into a single level object with joined keys

1 messages · Page 1 of 1 (latest)

fresh oyster
#

I want to write some basic types for my tailwind config since none of the libraries that exist work how I would like them to (and I don't have time to make my own right now). I would like to take the colours from my tailwind config and create a 1 level deep object that has all of the keys of the nested objects joined together with - to form the equivalent tailwind classes. So, for example, with the given object:

white: "#fff",
black: "#000",
neutral: {
    100: "#e6e6e6",
    200: "#cccccc",
    300: "#b3b3b3",
    400: "#999999",
    500: "#808080",
    600: "#666666",
    700: "#4c4c4c",
    800: "#333333",
    900: "#191919",
},

I would like an type to be generated that is:

{
    white: "#fff",
    "black": "#000",
    "neutral-100": "#e6e6e6",
    "neutral-200": "#cccccc",
    "neutral-300": "#b3b3b3",
    "neutral-400": "#999999",
    "neutral-500": "#808080",
    "neutral-600": "#666666",
    "neutral-700": "#4c4c4c",
    "neutral-800": "#333333",
    "neutral-900": "#191919",
}

So far I've got this:

type FlattenColors<T, Current extends string = ""> = {
    [K in keyof T]: T[K] extends string
        ? T[K]
        : Current extends ""
          ? FlattenColors<T[K], `${K}`>
          : FlattenColors<T[K], `${Current}-${K}`>
};

but obviously it isn't very close and I'm not sure how I should approach it

molten geyser
#

How deep can the original colors object be?

#

Seems that you want it to be recursive?

fresh oyster
vague karmaBOT
#
sandiford#0

Preview:```ts
type Prettify<T> = {
[K in keyof T]: T[K];
} & {};

type ColorsConstraint = { [key: string]: string | ColorsConstraint }

const colors = {
white: "#fff",
black: "#000",
neutral: {
100: "#e6e6e6",
200: "#cccccc",
300: "#b3b3b3",
...```

molten geyser
#

I think this is maybe 2/3rds of the solution

#

I have to leave you some work to do too, of course : )

fresh oyster
#

thats fine with me! the more i can learn the better. first i gotta understand what there is so far 😅

molten geyser
#

I grabbed the KeysMatching / KeysOfType helper from here

#

!hb distribution

vague karmaBOT
molten geyser
#

The only other thing that is tricky is distribution, and that is mainly because the TS syntax for it is not at all clear

#

It's a sort of hidden effect of using A extends B

#

But it just splits up A into the members of the union and applies the result to each member of union A separately

fresh oyster
#

is that something ill run into with this?

#

ah sure ok i see now

#

i hadnt gotten the comments of the second bit

molten geyser
#

Yeah you need it

fresh oyster
#

when you do K in (keyof T & (string | number)), what is the keyof T & (string | number) doing? (specifically the & (string | number)

molten geyser
#

keyof T is not restricted to defined keys in T

#

because object types in TS are not exhuastive, additional properties are allowed

#

So, try taking that part out and you see what happens

#

The object could have symbol indexed keys, and we can't put a symbol into a template string type

#
type KeysOfUnionOfObjects<T extends Record<string, string>> = T extends Record<string, string> ? keyof T : never
type KeysOfDistributedObjects = KeysOfUnionOfObjects<DistributedObjects>
#

here's another thing, you can add that at the end of the file

#

gets all of the keys of a union of objects using distribution again

fresh oyster
#

interesting okay thanks

fresh oyster
molten geyser
#

Hmm I think you need a way to combine a union of objects into one object

#

Oh its not quite working, maybe that code snippet I used was wrong

#

Oh well you might have to try to figure that bit out. It's kinda hard to turn unions into useful things

molten geyser
fresh oyster
#

not gonna lie im pretty stumped. i'm close but i've got a union in a place im not sure if i can get rid of it 😔
i didnt really follow what you put down after the todo as it just wasnt quite making sense in my head so it might not be what you expect

vague karmaBOT
#
dreaminginsanity#0

Preview:```ts
type ColorsConstraint = { [key: string]: string | ColorsConstraint }

const colors = {
white: "#fff",
black: "#000",
neutral: {
100: "#e6e6e6",
200: "#cccccc",
},
warm: {
foo: {
10: "#abc",
20: "#def"
...```

molten geyser
#

I feel like we might need a different angle to avoid the union

#

A mapped type somewhere feels like it could be an asnwer

molten geyser
#

Just need to make it recursive

#

This is a cool hack

#
type UnionToIntersection<U> = 
  (U extends any ? (x: U) => void : never) extends ((x: infer I) => void) ? I : never
vague karmaBOT
#
sandiford#0

Preview:```ts
type Prettify<T> = {
[K in keyof T]: T[K];
} & {};

type ColorsConstraint = { [key: string]: string | ColorsConstraint }

const colors = {
white: "#fff",
black: "#000",
neutral: {
100: "#e6e6e6",
200: "#cccccc",
300: "#b3b3b3",
...```

molten geyser
#

It's only useful where you have a union of objects

#

It won't help for selecting a specific member of a union

molten geyser
fresh oyster
#

just seeing if i can figure it out myself

molten geyser
#

😕

fresh oyster
#

didnt manage to get it to work it myself so i've resorted to your code - thanks for all the help!

molten geyser
#

Wouldn't mind seeing the solution, I might need it one day