Patterns
How do I turn `{ a: string } & { b: number }` into `{ a: string; b: number }` so tooltips are readable and equality checks pass?
A homomorphic mapped type walks every key once and rebuilds a single object. Modifiers survive; the intersection does not.
The recipe
type Prettify<T> = { [K in keyof T]: T[K] } type Base = { a: string } type Extra = { b?: number; readonly c: boolean } type Merged = Prettify<Base & Extra>
The build compiles this and checks each result below.
How it works
- 01
type Prettify<T> = { [K in keyof T]: T[K] }
It looks like it does nothing, and for a plain object it does. Handed an intersection it walks the union of both key sets and emits one object containing all of them.
What you get
Merged
→{ a: string; b?: number | undefined; readonly c: boolean; }Prettify<{ a: 1 } & { b: 2 }>→{ a: 1; b: 2; }One object where there were two. Nothing about assignability changed — only the shape, which is what `Equal` compares.
Prettify<{ a: 1 }> extends { a: 1 } ? true : false→true
Where it goes wrong
It flattens one level. An intersection nested inside a property stays an intersection, and applying this recursively will also expand every built-in type it touches — including `Date` and `Promise`, which is rarely what anyone wanted.
Takeaway
Intersections assemble an object type; a homomorphic mapping finishes one. Assembling without finishing is the most common reason a correct-looking type fails an equality check.