Created
April 7, 2022 12:25
-
-
Save funnylookinhat/1b1979ea9ce7e43431005082f6001374 to your computer and use it in GitHub Desktop.
Typescript Flatten Nested Interface
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Source: https://www.typescriptlang.org/play?noErrorTruncation=true&ts=4.4.2#code/HYQwtgpgzgDiDGEAEB5ATgSwOZIN4CgkikNgAXCNAMwWQDEB7BvQ4tqpgLiSjM2Cys2RAEYg0Afm7AArmBGUhw4NAoATbgWHakHBt178sAGiU6kaiBBgAbAJ4A5VRA0tz5sZOlyFaMzoBffyIg8xlgDAZgAz5SHAAfN3ciPRijU2TRcSkkWXlFc1DiIKUyOxh6JjobEDIKFTUkAF4katr6gB5GBgA+JQB6ACokMorWqpq6iAbmpPYuHliBAG5-ACIVXhcAOj01tLjV7XDI6MWjI+E1k6jdpn3zw-9PHLzfJETwyypSF0u2DbONTbSzWexOLbAzxrV4+SgfJBfCA-Br-YjXCK3aGw-JoBFIlF-JQBQb9fBKUgUai0cYMNpTBoAWRAshANjmwlSjwEwSQL28uN5gMhdwYD0McSFm3UIKstkcQO22IFvnJ5n6-RIVERmOAJCgSBA3KwnF5NzOEp5vI1Wp1p31hr1DBEACsIPAyKbzBjTqLxUtBN7zUrxDCVQVimriKNkAA1SgYKh2ADK4GQLUZMjIMjZ9gAogAPepqKBdCbtaYuYy0+n1FzM1k2PolSmUGiIWkcshgNQicN4xJaHRkDBkGwQTS8tgMTBYUhsg48zJILCUMAsnKWwOBXkiGRqVdkHGq8xoCAAN2mMggx4j2jIICwNl+m4DvIYl7Q54wEAA7q+jF5GA0AYNQZA9U4AGEGDAOAImgHIh2SDA1FvPxlxsBgsAYAAFWoAAsAMlZdQEgIil0yGdsFIaDwj4OxyO3bQAgAbQAXV5M9xxAKAIAAEVqG9FyY4RV2AM8oEY9jOLojAyP7ICGC2LxjQRJD3DUWoQE0HhwFsCdHTsJAAiQKdiHgKJ6jIAAVcoDK3XkihCJQ0FqOIdPgfD+2WJA0E9XI4TxEzBxGBgHxsftjKUTSKGTKxgByASKCUKgoAAazQpQwDAHIRCYbjgHwEoY1aSY6zUbpZlraYy16fAhhGOzSorBpKpadTu17bylDWTqRG2EcxwgbYqLnUAbH9C4er67YxIk7YAAZJqeAEZpgJSqRBLTtigPTxweFk7DRXqe369blO2CzyGmGy7OWlYetckcBEu-CHjeShjqeuJtj897Aq+tyXrIMK2X+3Fjpmwbx1mtcWTDVTPmAb5fjUSHTqVfdDwRj6Bx1FHUWmjGz0vWQIBxwL8WR5FUfR3sBsfZ8VARrcqYJolVoxj9KG-P8WYDNmacJzn6eA0DwJHKJoNglkfygBGCVponRZAsCIKlmC4LlxbthQincUFwk0eVs7VYlqDNdl6Adcw7C8LIN7GMNpWRdN8X1eAaWtethbtlI8mnaR9njdd7YxbVyXPct+CoB10aaIYOi0DsfmjGd4X0RmriIB4-jBNTuJ045zOMbm6AFepo26f6tBZMgfX3iDoXi6IE7RY2ygC4EIuQ+IGKIDi6YckVjOUnStCe7RbLcvynO9SbquiqQUlyRKvNyGT2ZcCQNKIDsYTq3PNlr24Q7qwYGBI4XPlZ5ZYzVnwNeC1sUCIA6ayelmAB9QsX8sd+kAQCLNMEsvkc5qCiPYQy7EkASDwEgNYS1uDWRYrjNixkkDIJ6A-EqP9n6YX-h-ZoShrKAOAcjA0zo3QelgfAliABpEgepd52AYNqaybEAC0UheSMKAcWA0rM4G-wIW-FB9C2Kf34SAg0pAqDwjzLQxR0iKFIHXvRWBvJ1LmBYdwAABgAElwPQgIRi8wsQAOQsIseglRoC1hrFoQ4zBCDthrFMbgcxVi942ICHojImQj42BPmoyxQTrw2ICckC+V8IqhO8XYGxZCBEIMcXA3AJk7EGhwhgeAaV37VnoZ-OBfBrwuJoDYXiLivExNOGyGxjleTSAvPCZpn4XEqE-MSFiLC2FIA4S47eujUmH2PgZay59L51LiRUqpJRH5NWgjYGoMBeIAKyWojedhP4tAABRKG3ixRRpA1FFlch6Ap8DalRGvrM5AARP48XidYtibFuBePCRAJJTkkAADIkB4T8hgNkHRDnHL1IWPgCAyCXO3tc8a3BSn3MeQaLxLy3nxM+d8voABKZJMimHyLxCgWhhzGEnN6dqFAGKUAMPQSZNpigFljGqsAd+OykBLJWWskRr92V9FXk1TM2Zcx2EhTI9Z5DQEAFVqzSvxao2M1ZYyzCIS0c8DAUJAA | |
// Saved here in case it ever goes away, this could be useful if you wanted to do some specific | |
// bindings between a react state that is flattened and a nested interface. | |
namespace Orig { | |
interface Foo { | |
foo: string | |
bar?: number | |
nested: { | |
foo: string, | |
deeplyNested: { | |
bar?: number | |
} | |
} | |
union: string | { | |
foo: string, | |
bar?: number | |
} | |
} | |
type FooFlattened = Flatten<Foo> | |
/* type FooFlattened = { | |
foo: string; | |
"nested.foo": string; | |
union: string; | |
"union.foo": string; | |
bar?: number | undefined; | |
"nested.deeplyNested.bar"?: number | undefined; | |
"union.bar"?: number | undefined; | |
}*/ | |
interface FooFlattenedManual { | |
foo: string | |
bar?: number | |
"nested.foo": string | |
"nested.deeplyNested.bar"?: number | |
// if union is a string: | |
union: string | |
// if union is an object: | |
"union.foo": string | |
"union.bar"?: number | |
} | |
type VerifySame = MutuallyExtends<FooFlattened, FooFlattenedManual> | |
} | |
interface Foo { | |
tmdb: number | { | |
title: { | |
original: string | |
german?: string | |
} | |
budget?: number | |
revenue?: number | |
tagline?: string | |
overview?: string | |
productionCompanies?: { | |
id?: number | |
logoPath?: string | |
name?: string | |
originCountry?: string | |
}[] | |
releaseDate?: string | |
genres?: string[] | |
runtime?: number | |
poster?: string | { | |
data: { sample: any } | |
contentType: string | |
} | |
} | |
rating: { ch: number; rt: number } | { total: number } | |
dateSeen?: Date | |
fsk?: number | |
mm?: boolean | |
} | |
type FlattenedFoo = Flatten<Foo> | |
/* type FlattenedFoo = { | |
tmdb: number; | |
"tmdb.title.original": string; | |
"tmdb.genres.0": string; | |
"tmdb.poster.data.sample": any; | |
"tmdb.poster.contentType": string; | |
"rating.ch": number; | |
"rating.rt": number; | |
"rating.total": number; | |
"tmdb.title.german"?: string | undefined; | |
"tmdb.budget"?: number | undefined; | |
"tmdb.revenue"?: number | undefined; | |
"tmdb.tagline"?: string | undefined; | |
"tmdb.overview"?: string | undefined; | |
"tmdb.productionCompanies"?: undefined; | |
"tmdb.productionCompanies.0.id"?: number | undefined; | |
"tmdb.productionCompanies.0.logoPath"?: string | undefined; | |
"tmdb.productionCompanies.0.name"?: string | undefined; | |
"tmdb.productionCompanies.0.originCountry"?: string | undefined; | |
"tmdb.releaseDate"?: string | undefined; | |
"tmdb.genres"?: undefined; | |
"tmdb.runtime"?: number | undefined; | |
"tmdb.poster"?: string | undefined; | |
dateSeen?: undefined; | |
fsk?: number | undefined; | |
mm?: boolean | undefined; | |
} */ | |
type Entry = { key: string, value: any, optional: boolean }; | |
type Explode<T> = _Explode<T extends readonly any[] ? { "0": T[number] } : T>; | |
type _Explode<T> = | |
T extends object ? { [K in keyof T]-?: | |
K extends string ? Explode<T[K]> extends infer E ? E extends Entry ? | |
{ | |
key: `${K}${E['key'] extends "" ? "" : "."}${E['key']}`, | |
value: E['value'], | |
optional: E['key'] extends "" ? {} extends Pick<T, K> ? true : false : E['optional'] | |
} | |
: never : never : never | |
}[keyof T] : { key: "", value: T, optional: false } | |
type Collapse<T extends Entry> = ( | |
{ [E in Extract<T, { optional: false }> as E['key']]: E['value'] } | |
& Partial<{ [E in Extract<T, { optional: true }> as E['key']]: E['value'] }> | |
) extends infer O ? { [K in keyof O]: O[K] } : never | |
type Flatten<T> = Collapse<Explode<T>> | |
type MutuallyExtends<T extends U, U extends V, V = T> = void |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment