Skip to content

Specifying supported TypeScript versions #32166

Description

@ehmicky

Search Terms

version
minimal version
minimum version
typesVersions

Suggestion

Specifying the minimal TypeScript version supported by a project.

With typesVersions, fallbacks can be specified for earlier versions. But we cannot specify which earlier versions are not supported.

Use Cases

Better error reporting for users using older TypeScript versions.

Examples

execa types only support TypeScript >= 3.4 because of how the readonly keyword is used.

There should be a way for us to specify this. One way would be to extend the typesVersions field to add this feature.

Our users would then get a clear error message similar to the one produced by the engines.node field for Node.js versions. Instead at the moment, we get GitHub issues from users using older TypeScript versions.

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.

Activity

  1. milesj commented on Jun 28, 2019

    @milesj

    I've been wanting this also. Maybe use engines for it also?

    "engines": {
      "tsc": "^3.4.0"
    }
    
  2. RyanCavanaugh commented on Jun 28, 2019

    @RyanCavanaugh
    Member

    This already exists. If your package.json doesn't have a compatible TypeScript version, you'll see

    "'package.json' does not have a 'typesVersions' entry that matches version '{0}'."

    We decided not to backport the typesVersions to prior compiler versions, but the problem will eventually go away once everyone is on a version that supports typesVersions

  3. ehmicky commented on Jun 28, 2019

    @ehmicky
    Author

    Ryan Cavanaugh (@RyanCavanaugh) this is great! However this does not seem to work for me. Steps to reproduce:

    $ npm install execa

    Manually edit (in node_modules/execa/package.json) the execa package.json to add support only for a future version of TypeScript:

      "typesVersions": {
        ">=4": { "*": ["index.d.ts"] }
      }

    Then using index.ts:

    import 'execa'

    This creates no errors:

    $ tsc index.ts

    TypeScript 3.5.2, node 12.5.0, Ubuntu 19.04. No tsconfig.json.

  4. RyanCavanaugh commented on Jun 28, 2019

    @RyanCavanaugh
    Member

    Just to check while you have it set up, does this happen if index.d.ts is named something else? I think we may have some fallback logic

  5. ehmicky commented on Jun 28, 2019

    @ehmicky
    Author

    If I rename index.d.ts to main.d.ts, tsc still produces no output. I tried renaming index.js to main.js as well.

    With the original name, the --traceResolution output is:

    ======== Resolving module 'execa' from '/home/ether/Desktop/index.ts'. ========
    Module resolution kind is not specified, using 'NodeJs'.
    Loading module 'execa' from 'node_modules' folder, target file type 'TypeScript'.
    Found 'package.json' at '/home/ether/Desktop/node_modules/execa/package.json'.
    'package.json' has a 'typesVersions' field with version-specific path mappings.
    'package.json' does not have a 'typesVersions' entry that matches version '3.5'.
    File '/home/ether/Desktop/node_modules/execa.ts' does not exist.
    File '/home/ether/Desktop/node_modules/execa.tsx' does not exist.
    File '/home/ether/Desktop/node_modules/execa.d.ts' does not exist.
    'package.json' does not have a 'typings' field.
    'package.json' does not have a 'types' field.
    'package.json' does not have a 'main' field.
    File '/home/ether/Desktop/node_modules/execa/index.ts' does not exist.
    File '/home/ether/Desktop/node_modules/execa/index.tsx' does not exist.
    File '/home/ether/Desktop/node_modules/execa/index.d.ts' exist - use it as a name resolution result.
    Resolving real path for '/home/ether/Desktop/node_modules/execa/index.d.ts', result '/home/ether/Desktop/node_modules/execa/index.d.ts'.
    ======== Module name 'execa' was successfully resolved to '/home/ether/Desktop/node_modules/execa/index.d.ts' with Package ID 'execa/index.d.ts@2.0.0'. ========
    

    It does fallback to index.d.ts (which means minimal TypeScript version is not enforced). This is the output when renaming files:

    ======== Resolving module 'execa' from '/home/ether/Desktop/index.ts'. ========
    Module resolution kind is not specified, using 'NodeJs'.
    Loading module 'execa' from 'node_modules' folder, target file type 'TypeScript'.
    Found 'package.json' at '/home/ether/Desktop/node_modules/execa/package.json'.
    'package.json' has a 'typesVersions' field with version-specific path mappings.
    'package.json' does not have a 'typesVersions' entry that matches version '3.5'.
    File '/home/ether/Desktop/node_modules/execa.ts' does not exist.
    File '/home/ether/Desktop/node_modules/execa.tsx' does not exist.
    File '/home/ether/Desktop/node_modules/execa.d.ts' does not exist.
    'package.json' does not have a 'typings' field.
    'package.json' does not have a 'types' field.
    'package.json' does not have a 'main' field.
    File '/home/ether/Desktop/node_modules/execa/index.ts' does not exist.
    File '/home/ether/Desktop/node_modules/execa/index.tsx' does not exist.
    File '/home/ether/Desktop/node_modules/execa/index.d.ts' does not exist.
    File '/home/ether/Desktop/node_modules/@types/execa.d.ts' does not exist.
    Directory '/home/ether/node_modules' does not exist, skipping all lookups in it.
    Directory '/home/node_modules' does not exist, skipping all lookups in it.
    Directory '/node_modules' does not exist, skipping all lookups in it.
    Loading module 'execa' from 'node_modules' folder, target file type 'JavaScript'.
    Found 'package.json' at '/home/ether/Desktop/node_modules/execa/package.json'.
    'package.json' has a 'typesVersions' field with version-specific path mappings.
    'package.json' does not have a 'typesVersions' entry that matches version '3.5'.
    File '/home/ether/Desktop/node_modules/execa.js' does not exist.
    File '/home/ether/Desktop/node_modules/execa.jsx' does not exist.
    'package.json' does not have a 'main' field.
    File '/home/ether/Desktop/node_modules/execa/index.js' does not exist.
    File '/home/ether/Desktop/node_modules/execa/index.jsx' does not exist.
    Directory '/home/ether/node_modules' does not exist, skipping all lookups in it.
    Directory '/home/node_modules' does not exist, skipping all lookups in it.
    Directory '/node_modules' does not exist, skipping all lookups in it.
    ======== Module name 'execa' was not resolved. ========
    

    It does not resolve execa but does not create any error messages.

  6. rbuckton commented on Jun 28, 2019

    @rbuckton
    Contributor

    Here's one thing you can try:

    1. Add a notsupported.d.ts file to execa.
    2. Modify the package.json for execa in the following way:
    "types": "notsupported.d.ts",
    "typesVersions": {
      ">=3.4.0-0": { "*": ["index.d.ts"] }
    }
    

    TypeScript versions earlier than 3.4 will load notsupported.d.ts which is empty. TypeScript versions for 3.4 onward will load index.d.ts. Attempting to import {} from 'execa' will report an error since notsupported.d.ts is not a module. However, import 'execa' will still work without error because this import form only imports the referenced file for side-effects and does not care whether the referenced file is a module.

  7. ehmicky commented on Jun 29, 2019

    @ehmicky
    Author

    Thanks for this tip Ron Buckton (@rbuckton)! It works but some issues with this approach could be:

    • the error message doesn't make it clear for the user that they are using an unsupported TypeScript version: File '/home/ether/Desktop/node_modules/execa/notsupported.d.ts' is not a module.. Many users will probably end up still creating GitHub issues thinking there's something wrong with the types declarations.
    • it requires creating an empty file, although that's not a big issue.

    More generally speaking this would be more of a workaround. Most TypeScript projects probably have a minimal supported TypeScript version, so it might be beneficial to have a "standard" way to specify it. This could also enable tools (linters, static analysis tools) to use it, in the same way that engines.node is currently used for Node.js versions.

  8. rbuckton commented on Aug 7, 2019

    @rbuckton
    Contributor

    If we were to add a feature to disallow older versions of TypeScript, it wouldn't catch TS versions prior to the version where the feature was introduced.

    It's not pretty, but another workaround would be to introduce a type error that could provide some context to the users that the TS version they are using isn't supported. Something like:

    // notsupported.d.ts
    type __Check<T extends ">= 3.6.1"> = T;
    type __NotSupported = __Check<"This version of TypeScript">;
    // error: Type '"This version of TypeScript"' does not satisfy the constraint '">= 3.6.1"'.
  9. ehmicky commented on Aug 7, 2019

    @ehmicky
    Author

    Thanks Ron Buckton (@rbuckton)! That does provide with a better error message, so this seems like the best workaround at the moment.

    Any feature related to this issue would indeed only work for users whose TS version is >= to the version that introduced that feature. I think this might still be a good idea even if it would take some time to be useful (since it requires most TypeScript users to migrate to at least that TS version first, which might take years).

    As a side note, I think the following might work as well, as a slight variation of the workaround above, maybe more explicit:

    "typesVersions": {
      ">=3.4.0-0": { "*": ["index.d.ts"] },
      "*": { "*": ["notsupported.d.ts"] }
    }
  10. rbuckton commented on Aug 9, 2019

    @rbuckton
    Contributor

    Keep in mind that won't catch TypeScript 3.0 and earlier

  11. 27 remaining items

  12. added
    DeclinedThe issue was declined as something which matches the TypeScript vision
    SuggestionAn idea for TypeScript
    and removed
    BugA bug in TypeScript
    RescheduledThis issue was previously scheduled to an earlier milestone
    on Feb 4, 2022
  13. RyanCavanaugh commented on Feb 4, 2022

    @RyanCavanaugh
    Member

    This can has been getting kicked down the road for quite a while and it seems like this isn't enough of a problem in practice to warrant a capital-f Feature for it. It's pretty straightforward at this point to have a pre-minumum-tagged file with the content

    // @ts-expect-error
    type RequiredTypeScriptVersion = "3.8";

    which would be sufficient for people to figure out what's going on.

  14. ehmicky commented on Feb 4, 2022

    @ehmicky
    Author

    I understand. Thanks Ryan Cavanaugh (@RyanCavanaugh) for explaining the rational behind this decision and for providing a workaround. 👍

  15. smaye81 commented on Aug 12, 2022

    @smaye81

    Has anyone implemented this approach? I can't seem to get it to officially work and wanted to see a working example if anyone has one.

  16. IvanGoncharov commented on Aug 15, 2022

    @IvanGoncharov

    Has anyone implemented this approach? I can't seem to get it to officially work and wanted to see a working example if anyone has one.

    Steve Ayers (@smaye81) I implemented it in graphql-js, see here:
    https://github.com/graphql/graphql-js/blob/743f42b6ef6006d35bf9e0b45e3b70d6e9100596/resources/build-npm.ts#L86-L97
    It produces an error like this:

    node_modules/graphql/NotSupportedTSVersion.d.ts:1:63 - error TS1003: Identifier expected.
    
    1 "Package 'graphql' support only TS versions that are >=4.4.0".
    
  17. smaye81 commented on Aug 16, 2022

    @smaye81

    Has anyone implemented this approach? I can't seem to get it to officially work and wanted to see a working example if anyone has one.

    Steve Ayers (@smaye81) I implemented it in graphql-js, see here: https://github.com/graphql/graphql-js/blob/743f42b6ef6006d35bf9e0b45e3b70d6e9100596/resources/build-npm.ts#L86-L97 It produces an error like this:

    node_modules/graphql/NotSupportedTSVersion.d.ts:1:63 - error TS1003: Identifier expected.
    
    1 "Package 'graphql' support only TS versions that are >=4.4.0".
    

    Great thank you Ivan Goncharov (@IvanGoncharov) !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    DeclinedThe issue was declined as something which matches the TypeScript visionSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions