Repository: TypeScript
Issue: Suggestion: Provide a way to force simplification/normalization of conditional and mapped types
Labels: Suggestion,Awaiting More Feedback

# Suggestion

<!--
  Please fill in each section completely. Thank you!
-->

## 🔍 Search Terms
simplification
normalization
normal form
eager evaluation

The closest I found to this was https://github.com/microsoft/TypeScript/issues/20267
<!--
  💡 Did you know? TypeScript has over 2,000 open suggestions!
  🔎 Please search thoroughly before logging new feature requests as most common ideas already have a proposal in progress.
  The "Common Feature Requests" section of the FAQ lists many popular requests: https://github.com/Microsoft/TypeScript/wiki/FAQ#common-feature-requests

  Replace the text below:
-->
Eager
normalize
normalization
simplify

## ✅ Viability Checklist

<!--
   Suggestions that don't meet all these criteria are very, very unlikely to be accepted.
   We always recommend reviewing the TypeScript design goals before investing time writing
   a proposal for ideas outside the scope of the project.
-->
My suggestion meets these guidelines:

* [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
* [x] This wouldn't change the runtime behavior of existing JavaScript code
* [x] This could be implemented without emitting different JS based on the types of the expressions
* [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
* [x] This feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals).

On the design goal point, I believe this feature would further goals 5 and 9:

- Produce a language that is composable and easy to reason about.
- Use a consistent, fully erasable, structural type system.

Elaboration on this point is below.

## ⭐ Suggestion

Add a way to force eager evaluation of mapped and conditional types, or a way to force evaluation of a type until it reaches a sum-of-products normal(ish) form. This would lead to simpler inferred types from a user's perspective when type algebra is in play, as well as more terse and focused error messages.

I think there are multiple ways this could be done. There could be an `eager` keyword which, when attached to a conditional or mapped type, causes that type to be evaluated as soon as possible, discarding intermediate type information. It could be a baked-in "utility type", like `Simplify<T>` or `Simplify<T, depth extends number>` which, when applied to a type, forces the evaluation of that type (until it reaches a sum-of-products form, or for a given number of steps).

## 📃 Motivating Example

Here is a [small example](https://www.typescriptlang.org/play?#code/FAFwngDgpgBAQgQwE4wLwwN7BjA9gOygC4Z8BXAWwCMokAabGEAd1xPOtoZxAAskoxUpRpJgAX2DAA9NJgAxJLgoxeIEBADORWZpAIAxgGtcAN1oAzADa5mAOgPLpARzJQ9ASwKbpAFgAcAEwAzAAMAOzhwdK8tgC0ILhxBgIIIFBxCHEQyCAeCFZxVh5GGXxpcQKuHgKamXGaHvgA5lYZEErQSOAJSTQNUCCgkLAASlDVAgCCIAAyUAh6APKEADwAKjBQAB7p+AAmmjDjjkj7q3pITc10MGT4Rvi2+AB8L2iMmDAA2gDSME0YKUwLgLDB1gBdOIAfhI40mUHOAAUPMYNrdfm8YAAyGBI3L5KyrFFo9a3ACi2wMVjI+ygq2BoPBGLeLwA3DBxN9GWDIVJwNAYDN5osQCsoIgUOh4WQalBhQtlmtJeyZAAqYDrXiwAWwbUCAFHRwUCBtbZbJ5kZq8Ji8NLggDKMH2uHc+AA5CAYBYyFYrGAttscgcASAiNgABQyuXI1FGVaS24AIgIUCTWNx+O6hOJcYTyGTLFwSZgAB8YEm+AI028AJRlmCR6MCWNoxMVovp96ZgkFVZ2AcvevlqMTWUt-uDnF43tEgd2IfANXSKSyGAACVosA8Rz4sDHHlMBSg+C9utuzF4qJtO5gl-tAEk7whT0xcHdNNv8DAKAHHHTw11IU5kVMVCElAARdwYzQTBGFTdgRC4Rgi1hYROHoFD+EENCOFERtxAbLAcFTXCkMwnhWEQjDuFtasyIwiQiPgwgGNEWjUOo9isOrLjaAkVc5A6XAmXKM9tSYVgYGQfdqiPNpT3DH18AMPICGk1SyAKB0yCoXUlgsKDGhbCMLBIBVRXFSVa3MkDLPA5AjJg4iYAEEAyCQb8LDZATgGU1SvG-OljMRHS9JGAypk0gpTNskUlQlRzoJbGzgPisDEpQFy3I8ryfMkIA) of a case where a utility type leads to much less useful intellisense information than would be present with a manually-written type. My proposal here would be a way to force the type `AtLeastOneBar`, which has inferred type
```typescript
 (Required<Pick<Bar, "one">> & Partial<Pick<Bar, "two" | "three">>) | 
 (Required<Pick<Bar, "two">> & Partial<...>) | (Required<...> & Partial<...>
```
to simplify to the equivalent type,
```typescript
{
  one: number,
  two?: number,
  three?: number 
} | {
  one?: number,
  two: number,
  three?: number
} | {
  one?: number,
  two?: number,
  three: number
}
```

This case may be a matter of personal taste, but I chose it because it is comparatively simple. It may be that users would prefer to prevent *any* expansion of this type at all, and have the following inferred type:
```typescript
RequireAtLeastOne<{
  one: number,
  two: number,
  three: number
}>
```
I think this difference in preferences is a valid point of discussion, because it's also on the topic of "how can library authors exercise more control over the types produced by their utilities?"


[Here](https://www.typescriptlang.org/play?#code/PTAEBUAsEsGdTqAhqW0C2AHANtAZtAKYAmo6A9sYdqOXsqgMaSHoq4BGATklwJ6gA7jGagArrELxCADwAuhAHZoO2QskWLycpHOjllAKBC1FoAtWLw55UIwM7oZp3nJc2eg6FwBrdeUxPRSRcOT4AGlAeORYuUBikM0heYgBae2InAHNjMHRCGMp4X3UAAwA6AKCQgAoASgrQAEF4FC4pMWw5SLQsxXxoRkS5bAEKdvi+THUQrMJuFER2gEcxaHbiQ1yIFiixRT18uwNYOS4xRjl45KvIcmwrJhY2UGJdJHLtqHUw6ajCTDtSQHEigDgCGLqWDMViLeBDLhcIikcHXdSYZIHcjoSZ-cCGWSYNxXRjYJCweAAZRhbAAPOBIk1NNpdPplKBZApFI8AEqEexcYi005IxRZSL7HxaQSKAB8stAAG9DKBQJgxKpBv8kMQDKM1ZibOgAFwQADcKrVGtwjG1usU+sSWh0QVgpr5AqFIuyEsUUvIMtlFst9mUZwuNi4NQxw2xpoZGmdrJOAH5TUyk666krLaqYnByjGsTiALwG2PoC2qvMwWDlJ0s12gMsNl1s+AAHw7SoAvlXQD2tqr1ZrbXgyVlaQBpQgCTlKR7esWymq50B+Pimmd8S11U3U55IemM5ltk6gABkoA9biF28i4cI8pz1f+cjEXDMikIglAB9hq6vjWBZFka4RrqqADyHAAFb8nI9YUtAfQ1IqPYPrW9ansmyiRIqoAANobgAuqaj4DnUa51P2g6DtslI6NyKTiHooQCK4cTQoe3jQAsSJSGa3gFPAEiEFw8BzFckLIJcYghLi6jtICUhKAomwmKiKBcbC5SgAA6jM2CwLYonXIgThoq8hAEP0QS0PQ5AfqApTQTBABiRAPLApQsdAbFgtQAaGL86gAJKKHgYm0pSHLyAuVI0kekrSooeE9s+ZYxfO3IJYetIuGJEBpQqKYQKAprfgAbmJwZfLs5wHBg6h4PslztuYbjHGG5xtWKTywnWoDhWZ8BUBQkSOZxiWtBMuDoNAanxLYVDQkiHDZNsHDkPchCJK03K0LB8HwIIC2QGC21qIk5ieVYhgtYobVeFt9z1PuiW0i9V2pb2CrKq+7Tvp+oDfr+-5sDU+xUDZoLkhoES9pRg4mAActoPy7C5sEeZY3m+f5IWgLEhC6VA5kncTyDYDQdCWYTszzDwPHVfAFn5qNrDkOBJiJKQk12Mk1NKHMCDWLYghIgoCBXBZsAXOdKCCEgEI3EI6iSaAWTbaQhMFe0j0k-drV2eQsH0rFXK8vyt7CmcPp-h9yUBj9aHyiuTjqnI8Z7g7eWuTjXn0rKxUvtWJihVgaj5AcOEcrgVCbADBQfmYUPWU4sP7XwFp0SYAASAZ2NdgjqNDGfOf7t143LogJFJux6xsCnbLTbnbaLCBUI1Qw0DY1y6PZoAACJSOsJBt+QpV52JhAAOQUwPoVmX6HfSRQpyoGcSDIZAcgcUrgrbEgmCAuQSDMMaWwmAA4gUDC4BvtPCIM50bjNimEKsY+bITfJfxsAcrDm2ylbT0ttRTil9rCWkTsZSRBvIKcB9svq7TlG7Zslp-rVgIlOBAZgNy03AKRCAODiIW3ilAuksCfp62aNhV0JUIJ0MzO1EB8B8JVDZCEMi5x1A9lAKVKqhUtxMIqoQaqXBLQ9iIrOQhxEr5gAMkIPyNBvygj7u0Cg1VLKlHBkgHyEtj7TDiHgLg2JaBOW-KcUEFgvLBSmOoAAqooQxmBgFxRytea2iClyQL0TAv0KVIiJD4OgksmC1w4LweuWR9AiHxlIeQzx-jaEAAVDTYmCYoUJAjQDpIrOVEG4ixJSODCYAAskgPwGhDpwUuMomIVMaC2MeJwgwIQsmkGuqbOpVxTqNJWGsJuLT4CDO-tzMAkJnAgnEvBNE6B7F-Errjdxlt4AIK9HbMUkR-HUPgd4zZEDIgoMSG7BU4TVSpN4HoEItJnGuKDgqK8f8hkkFpKkwYPg7kuJ4G48AwcvH-3HlXR5QYtgKOvPsQ46gJBIDmJERg7RWR9RQBwckWptJsEMKGDerhbBlh6ahNcSBTRfXqOBV8HBSWXXqOUcccKagACI2nBGwIypGlF6KMTeIKcQsA4XNU6pi9gvEeD8VgIJaShMJ4d2EAPUS4llHUz5eoJw2xITrGQCfG0scMgkyGjLaQeBIptWqvqaSCrUh4HPtkZoqSl602ksKnifEIhHwOsZMgEgrgcBmPANAkc-VqnIEhVQhBFnqBlWWcKkUuC0hCrTPFYLjAACp9LqGSNo6SrhqYBltY3dRDiOpxAnpfK5XAbnYG+Q8rBqoSWUKPCcl26V+yqipQ2z6l1UH7LAcywIXC2UPl4fKHOz5nmf1eUKD5jAvn3N+bSWtyB3p5SbcVVtYJl3QNXV43tLKQiMqHWIJ8YL0qREZUgRl8othk3gFtQUYl4BeCcPYLA7QWDKF4moSIUhpiMGgCEfUWRoDVTMHXaWHcJ3AZCKpYKthSgjzQAA7aPlfXYADJ8UAKawDgpMOAKQJJyRSDIrWVeuxCYl2QBMM4fBbV9xWoi3iMwtrVQjcPUeSH8WhzrdSnaiQKXVg4GmC6vHFCGFzmAVJZis2q35nMb80RC1-EQJByq0GDhLQsXEFa39m4PSemYc+74QiUg1CFBD38ah4FNBPH25mONcbfCncwOcjaPTstpjYJmOAhSaLJWoVm2OIfHttH2MrF2Ayc3gFzEKACiMgkCBsffQQmYkzHiVJrsVLnUOKWSGJIPltGi0MwWGqIEqlrC7AVdsfpCtNZIquFQeStNoS8D4KkND5AfC2v2N+RgUh+X8DwRxDwbJL44quFlrgABGaz7cyz4XbeRPsqbDDgCLbPBbPCj2CR7LPDuzpkBIT6EgMNGnCaz2WYHfC9b-HbtdoJdtt2u18Z3TbPt1RB3xGHUGAcspZ4YbyWY4xYRQCzyQHtxA80kJ9TZutzbX3tsDj2xwMQVwxlN1h38S0s8Xnf3eZ86t87rubrpHdltlpHsfW3Rs2k72B0HoR8enbALz2Xr++UGoAAmAAzJzzn2ZDBYdi4ibLnUnUJahAtOSQRIg1ZqSptTUkHFjZOBNkXXBOemjs8Fzj8OlsWmACmwwkmAhiRB2DiH8AodoBh6BuHG7Gc7eR6j-4QLSCY-ULPbXxAJ7-a5wAdgACxTcokL8FQA) is a much more advanced example, and a case where I don't think the need for simpler types is as subjective. This is a simplified excerpt from a library which I am building, which aims to do a similar job to Runtypes/Zod/etc., but with extra extensibility. The type algebra in the library is hairy, and I do not know whether I will complete it because the complexity of the inferred types is too high for the library to be practically useful.

In this example, I define classes to describe schemas at runtime and link them to TS types. I show two types, which are equivalent. (They are subtypes of each other.) One is the manually created type,
```typescript
{
    a: boolean,
    b?: boolean
}
```
and one is the library-inferred type
```typescript
Partial<Unwrap<{
    a: Schema<boolean, {}>;
    b: Schema<boolean, Record<"optional", true>>;
}>> & Required<Pick<Unwrap<{
    a: Schema<boolean, {}>;
    b: Schema<boolean, Record<"optional", true>>;
}>, "a">
```
The inferred type leaks implementation details to all of the library's users. It makes the types way harder to understand and leads to intimidating type errors. The goal of this issue is to provide a way to make the actual type normalize down to the desired type inside of the library.

## 💻 Use Cases

I believe this feature would address a common complaint about use of TS's advanced features: "Avoid type algebra because it adds complexity and is hard to reason about". From the typechecker's perspective, types compose. From a user's perspective, reasoning about types is not compositional due to progressively increasing complexity. It's pretty easy to use type algebra to make several types which are comprehensible on their own, but which are incomprehensible when composed. As a result, some users (very persuasively) advise against using type algebra anywhere, because doing so can lead to complexity which ripples across a codebase.

In my opinion the problem is that it's not possible to write abstractions which encapsulate the complexity of type algebra. The situation is like if JS was lazily evaluated, and if using `console.log` on a value for debugging printed out a long series of thunks reflecting all of the computations which lead to that value instead of the value itself. If we could force type algebra to be simplified, it could make type-level programming easier to debug and maintain, and it would allow library authors to hide the fact that type algebra is occurring at all from end-users.
<!--
  What do you want to use this for?
  What shortcomings exist with current approaches?
  What workarounds are you using in the meantime?
-->