Skip to content

More consistently backport type system fixes #38237

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
5 tasks done
AnyhowStep opened this issue Apr 29, 2020 · 2 comments
Closed
5 tasks done

More consistently backport type system fixes #38237

AnyhowStep opened this issue Apr 29, 2020 · 2 comments
Labels
Declined The issue was declined as something which matches the TypeScript vision Suggestion An idea for TypeScript

Comments

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Apr 29, 2020

Search Terms

backport, fixes, type system

Suggestion

When a type system bug is introduced in version X, and later fixed in version X+n,
the fix should be backported all the way to X (or at least a reasonable number of past releases).

Use Cases

This feature request is mostly for library authors with source code in native TypeScript.

When a new version of TypeScript is released, library authors have to scramble to check that it doesn't break their .d.ts files (handwritten or tsc-generated). If it is broken, they have to somehow fix it so that it works on existing versions of TS, and also the new version.

Over time, the result is a lot of duplicated .d.ts files that may go out of sync, and are prone to mistakes.

Surprisingly, JS library authors will have an easier time with this problem, since they will probably already be writing their own .d.ts files and use typesVersions.

Examples

export type AnyExtendedMapper =
    ExtendedMapper<any, any, any[]>
;
export type ExtendedMapper<HandledInputT, OutputT, ArgsT extends any[]> =
    (name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT
;

declare function extraParamsOfImpl<F extends AnyExtendedMapper> (f : F) : F;

declare const mixed_1: ExtendedMapper<unknown, never, []>;

export const test_1 = extraParamsOfImpl(mixed_1);
//                                      ~~~~~~~
//                                      3.3.3 works
//                                      3.5.1 does not work
//                                      3.6.3 does not work
//                                      3.7.4 does not work
//                                      3.8.3 works

type ExtraParamsOfImpl<F extends AnyExtendedMapper> = F

type Test = ExtraParamsOfImpl<ExtendedMapper<unknown, never, []>>
//                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//                            3.3.3 works
//                            3.5.1 does not work
//                            3.6.3 does not work
//                            3.7.4 does not work
//                            3.8.3 works

Playground

Since the fix in 3.8.3 was not backported to 3.5.1, 3.6.3, 3.7.4, my options are the following,

  • Only support 3.3.3
    This is bad because over time, the library will just become more and more irrelevant

  • Only support 3.8.3
    This is bad because it alienates old users, or extra effort has to be put in to backport new features

  • Find a way to write the type such that it works on all versions of TS
    This is bad because it is not always possible, or may require significant effort to shuffle types around.
    And you also have to write compile-time (type-level) tests for each TS version you want to support, to make sure the fix works for all versions of TS.

  • Write a custom .d.ts file for 3.5.1, 3.6.3, 3.7.5 and use typesVersions
    This is bad because you now have to write one set of .d.ts per TS version.
    Requires compile-time tests for each handwritten set of .d.ts files, and tsc-generated files.
    Very error-prone.

  • Use downlevel-dts, hope it does not produce broken emit for older TS versions, and use typesVersions
    Also bad because "generate output that does not break on older releases due to introduced compiler bugs" is probably not a use case downlevel-dts was intended to solve.
    Generated output still needs to be fixed by humans.
    Requires compile-time tests for each set of downlevel-dts .d.ts files, and tsc-generated files.
    Very error-prone.


In some cases, it's possible that an entire library becomes completely useless because type system fixes were not backported, and new bugs were introduced.

  1. TS 3.3.3 is OK
  2. TS 3.5.1 introduces type system bug A
  3. TS 3.6.3 fixes type system bug A (not backported)
  4. TS 3.6.3 introduces type system bug B

The library is stuck on TS 3.3.3 because 3.5.1 has bug A, and 3.6.3 has bug B.

The library author has to somehow find a workaround for both bugs to support 3.5.1 and 3.6.3.

The problem gets worse as new versions are added.


I'm actually running into such a problem, at this point.

Exhibit A: Playground

Exhibit B: #38235

Bugs with each TS version,

  • 3.3.3: -NA-
  • 3.5.1: A
  • 3.6.3: A
  • 3.7.5: A
  • 3.8.3: B

So, I'm stuck on TS 3.3.3 because any later version gives me some kind of bug I have to work around.


In other cases, a project may find it impossible to update their TS version.

  1. TS 3.3.3 is OK
  2. TS 3.5.1 introduces type system bug A
  3. TS 3.6.3 fixes type system bug A (not backported)
  4. TS 3.6.3 introduces type system bug B

The project is stuck on TS 3.3.3 if library L triggers bug A, and library M triggers bug B.

  • Upgrading to TS 3.5.1 breaks L.
  • Upgrading to TS 3.6.3 breaks M.

The project now needs to wait for a new TS version that fixes both A and B in the same release, or wait for L and M to find workarounds... Or find other libraries that support the new TS version.

The more libraries the project uses, the greater the chance of being unable to update their TS version.


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, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

I understand that asking for type system fixes to be backported many versions increases the burden on the TS team, but it makes life easier for library authors.

And also makes life easier for library consumers, I suppose.
Imagine filing a bug report like,
"Your library does not work for TS version X",

and then being told,
"Downgrade TS to X-k".

And then downgrading causes errors for 10 other libraries due to other compiler bugs with fixes that were not backported.

@RyanCavanaugh RyanCavanaugh added Declined The issue was declined as something which matches the TypeScript vision Suggestion An idea for TypeScript labels May 1, 2020
@RyanCavanaugh
Copy link
Member

I appreciate the problems here, but this would be an incredibly large amount of work; we simply are not staffed to do this for anything but the most critical fixes. Often these "fixes" end up breaking other things as both intentional and unintentional side effects; we'd have to have some kind of epicyclic versioning scheme like "This is 3.6 plus the 3.9 fixes that we know cause certain other breaks", which is somehow neither a strict 3.6.x nor a 3.9.x release.

@AnyhowStep
Copy link
Contributor Author

That's fair.
I knew it would be a massive amount of extra work but wanted to try bringing it up, anyway.

Thanks for looking at it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Declined The issue was declined as something which matches the TypeScript vision Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

2 participants