Elnur
Elnur

Reputation: 93

How to deep flatten an interface in Typescript?

for example:

interface a {
  name: string 
  nested: {
    lastName: string 
    nested: {
      age: number
    }
  }
}

I want that to become:

interface b {
  name: string 
  lastName: string 
  age: number
}

interface values will be dynamic, so I probably need some kind of recursive solution to this

I only ended up with finite solutions

Upvotes: 3

Views: 2950

Answers (1)

kaya3
kaya3

Reputation: 51044

Here's a solution using mapped and conditional types: FlattenPairs constructs a union of pairs like ['name', string] | ['lastName', string] | ['age', number], then Flatten converts this into an object type. I assumed that the nested properties are not necessarily all named nested (and do not necessarily have predictable property names), so the type Primitive is needed as the base case for the recursion.

type Primitive = string | number | boolean
type FlattenPairs<T> = {[K in keyof T]: T[K] extends Primitive ? [K, T[K]] : FlattenPairs<T[K]>}[keyof T] & [PropertyKey, Primitive]
type Flatten<T> = {[P in FlattenPairs<T> as P[0]]: P[1]}

Example:

interface TestInterface {
  name: string 
  nested: {
    lastName: string 
    nested: {
      age: number
    }
  }
}

// {age: number, lastName: string, name: string}
type Test = Flatten<TestInterface>

Playground Link

The helper type FlattenPairs can be written in a slightly simpler way if the nested property name(s) are known:

type FlattenPairs<T> = {[K in keyof T]: K extends 'nested' ? FlattenPairs<T[K]> : [K, T[K]]}[keyof T]

Playground Link

Upvotes: 8

Related Questions