Description
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 unionTypeScript.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 toTypeScript.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:
- 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
{}
toACR[T]
- but only in TS versions above 4.8. The fix breaks in older versions. - Behavioral changes depending on
tsconfig.json
settings. Imagine a mapped type with functions.
The user specifiessomeMethod(action: PayloadAction<string>): void
and we map that over to asomeMethod(arg: string): void
.
In the case the user specifiessomeMethod(action: PayloadAction<string | undefined>): void
, we map it over to an optional argument in the formsomeMethod(arg?: string): void
Now assume the user hasstrictNullChecks: false
in their tsconfig. Suddenly everything falls into the second case. - A library might for example also behave differently if the user has
noUncheckedIndexedAccess: true
in theirtsconfig.json
This leads to quite a few weird hacks in library types that might essentially break with every new release.
- 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 monstrouspackage.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;
- 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 :)