Skip to content

Feature Request: Expose TS configuration and internals as TypesΒ #50196

Open
@phryneas

Description

@phryneas

Suggestion

πŸ” Search Terms

expose CompilerOptions types

βœ… Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's 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
'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

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, Nightly bug(?) with infering function generic while being part of intersectionΒ #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
import('@phryneas/ts-version').TSVersion.AtLeast<4, 8> extends true
      ? ACR[T]
      : {}

In the past we used hacks like

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

type WithStrictNullChecks<True, False> = undefined extends boolean ? False : True;
  1. 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 :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    In DiscussionNot yet reached consensusSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions