Repository: TypeScript
Issue: Feature Request: Expose TS configuration and internals as Types
Labels: Suggestion,In Discussion

# Suggestion

## 🔍 Search Terms

expose CompilerOptions types 

## ✅ 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).


## ⭐ Suggestion

I want to suggest that TypeScript would add some global namespace that contains information about the current running setup:

* TS version (having some `AtLeast` type like I showcase in our "hacked use cases" below would be great too, but if we know the major and the minor we can also do that in userland)
* active `compilerOptions` flags - it would be amazing to have some string union `TypeScript.CompilerOptions.ActiveFlags` where we could just do 
```ts
'strictNullChecks' extends TypeScript.CompilerOptions.ActiveFlags ? SomeBehaviour : SomeLessSafeFallback
```
* the current `compilerOptions` in general - I don't know why someone would need access to `TypeScript.CompilerOptions.allowSyntheticDefaultImports` but it would feel like a waste not to expose that


## 📃 Motivating Example

```ts
type MessageFromTypeScript = AtLeast<[TypeScript.Version.Major, TypeScript.Version.Minor], [4, 8]> extends true 
? "it's the future" 
: "you live in the past"

class MyRecord<T>{
  get(index: string): 
    'noUncheckedIndexedAccess' extends TypeScript.CompilerOptions.ActiveFlags 
    ? (T | undefined) 
    : T
}
```

## 💻 Use Cases

As library authors, we currently have to apply quite a number of hacks to support as many versions of TypeScript and as many different user configurations as possible.

Some example problems we are facing:

1. Slight behavioral changes between TS versions. For example, #49307 forced us to change a `{}` to `ACR[T]` - but only in TS versions above 4.8. The fix breaks in older versions.
2. Behavioral changes depending on `tsconfig.json` settings. Imagine a mapped type with functions.
  The user specifies `someMethod(action: PayloadAction<string>): void` and we map that over to a `someMethod(arg: string): void`. 
  In the case the user specifies `someMethod(action: PayloadAction<string | undefined>): void`, we map it over to an optional argument in the form `someMethod(arg?: string): void`
  Now assume the user has `strictNullChecks: false` in their tsconfig. Suddenly everything falls into the second case. 
3. A library might for example also behave differently if the user has `noUncheckedIndexedAccess: true` in their `tsconfig.json`

This leads to quite a few weird hacks in library types that might essentially break with every new release.

1. Could be solved with a `typesVersion`, but honestly: if we can avoid having two complete sets of types, we want to avoid that. We had it for a while for pre-4.1 types and post-4.1 types and maintaining it was a pain. Right now, I've published https://github.com/phryneas/ts-version/ which uses a monstrous `package.json` with typesVersions just to export the current TS Major and Minor.  
So we solve our problem with an additional dependency and 
```ts
import('@phryneas/ts-version').TSVersion.AtLeast<4, 8> extends true
      ? ACR[T]
      : {}
```
In the past we used hacks like
```ts
export type AtLeastTS35<True, False> = [True, False][IsUnknown<ReturnType<<T>() => T>, 0, 1>]
```
for that.
For a while we also had a subfolder with a `package.json` that would only use `typesVersions` on one import in that subfolder. At least we did not have to maintain two complete separate type definitions, but let's say it was not a great developer experience.
2. We check for `strictNullChecks` with 
```ts
type WithStrictNullChecks<True, False> = undefined extends boolean ? False : True;
```
3. We are not doing this yet but I'm sure we'd find a way to *somehow* detect `noUncheckedIndexedAccess` as well.


All that said: we have problems and we have solutions. But our solutions feel horrible and wrong. And also, having a package around with 120 different `typesVersions` entries just to detect the current TypeScript version can't really be good for performance.

I hope you're going to give this at least some consideration :)