Typescript Get Undiscriminating Union Object Type from Array of Object Literal

Acid Coder
2 min readJun 22, 2022

--

Sometimes we don’t have the type, but we have the array value, and we want to get the element type

const obj = [{size:”big”, color:”blue”},{size:”small”, color:”red”}]

To get the element type, we can do this, first assert the array as const, then

const colorAndSize = [{size:”big”, color:”blue”},{size:”small”, color:”red”}] as consttype ColorAndSize = typeof colorAndSize //[{ readonly size: “big”; readonly color: “blue”;}, { readonly size: “small”; readonly color: “red”;}]type DeepMutable<T> = { -readonly [P in keyof T]: DeepMutable<T[P]> } // remove readonly modifiertype GetElementType<T extends readonly Record<string, unknown>[]> = DeepMutable<T[number]> // { size: “big”; color: “blue”;} | { size: “small”; color: “red”;}const abc:GetElementType<ColorAndSize> = {size:”big”, color:”blue”} // okconst efg:GetElementType<ColorAndSize> = {size:”big”, color:”red”} // error

playground

we end up with a discriminating union and this may not be ideal because we cant mixed the types, eg we cannot have big and red.

here is how we compute the undiscriminating union

const colorAndSize = [{size:”big”, color:”blue”},{size:”small”, color:”red”}] as consttype ColorAndSize = typeof colorAndSize //[{ readonly size: “big”; readonly color: “blue”;}, { readonly size: “small”; readonly color: “red”;}]type DeepMutable<T> = { -readonly [P in keyof T]: DeepMutable<T[P]> }; // remove readonly modifiertype GetUndiscriminatingElement <T extends Record<string,unknown>[], U extends T[number]=T[0]>= T extends [infer H, …infer Rest]?
Rest extends []?{[key in keyof H]:U[key extends keyof U ? key:never]|H[key]}
: Rest extends Record<string,unknown>[]?GetUndiscriminatingElement<Rest,{[key in keyof H]:U[key extends keyof U ? key:never]|H[key]}>:”impossible route”
:”impossible route”
type A = GetUndiscriminatingElement<DeepMutable<ColorAndSize>> // { size: “big” | “small”; color: “blue” | “red”;}const a:A = {size:”big”, color:”red”} // ok

playground

limitation: the length of the array cannot exceed 999 because the max depth of TS recursion is only 1000

update: simpler and not bounded by recursion depth, thanks to captainyossarian

const colorAndSize = [{size:"big", color:"blue"},{size:"small", color:"red"}] as const

type ColorAndSize = typeof colorAndSize //[{ readonly size: "big"; readonly color: "blue";}, { readonly size: "small"; readonly color: "red";}]

type GetUndiscriminatingElement <T extends readonly Record<string,unknown>[]> =
{ -readonly [P in keyof T]: T[P] } extends [infer H,...infer Rest]
? { -readonly [Key in keyof T[number]]: T[number][Key]}
: "you forgot to assert as const"

const A :GetUndiscriminatingElement<ColorAndSize> = {size:"big", color:"red"} // ok

playground

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Acid Coder
Acid Coder

Written by Acid Coder

Typescript Zombie. Youtube Pikachu On Acid. (Unrelated to programming but by watching it you become a good developer overnight)

No responses yet

Write a response