Skip to content

The TypeScript 3.1 "typesVersions" setting in package.json should use an array or disallow version overlaps #27417

@nonplus

Description

@nonplus

Search Terms

typesVersions, 3.1

Problem

Using an unordered object for the typesVersion setting in package.json is brittle as the implementation depends on the behavior of JSON.parse and how keys in an object are iterated over.

FWIW, using an object made sense before version ranges support was added to typesVersion.

Suggestion

Change typesVersions setting package.json to use an (ordered) array of configurations rather than an (unordered) object. For example:

{
  "typesVersions": [{
    "version": ">=3.2",
    "paths": { "*": ["ts3.2/*"] }
  }, {
    "version": ">=3.1",
    "paths": { "*": ["ts3.1/*"] }
  }
  ]
}

Alternatively, you could keep the current format, but remove the ambiguity by disallowing overlapping version matches. So the example in the 3.1 docs would be invalid and treated as error when compiling under TypeScript 3.2. A corresponding valid configuration would look like this:

"typesVersion": {
  // NOTE: order doesn't matter :-)
  ">=3.1 <3.2": { "*": ["ts3.1/*"] },
  ">=3.2": { "*": ["ts3.2/*"] }
}

This would require getPackageJsonTypesVersionsPaths in src/compiler/moduleNameResolver.ts to check all the typesVersion configs and throw an error if there is more than one match for the current TypeScript version.

Backwards Compatibility

For backwards compatibility of an array configuration, the 3.1 object configuration could still be supported, but should be deprecated.

More Info

The JSON specification says that:

An object is an unordered set of name/value pairs.

However, the typesVersions setting in TypeScript 3.1 treats it as an order set. From the docs:

Since ranges have the potential to overlap, determining which redirect applies is order-specific. That means in the above example, even though both the >=3.2 and the >=3.1 matchers support TypeScript 3.2 and above, reversing the order could have different behavior, so the above sample would not be equivalent to the following.

{
  "name": "package-name",
  "version": "1.0",
  "types": "./index.d.ts",
  "typesVersions": {
    // NOTE: this doesn't work!
    ">=3.1": { "*": ["ts3.1/*"] },
    ">=3.2": { "*": ["ts3.2/*"] }
  }
}

Use Cases

  • Removes dependency on undocumented object key iteration order
  • Won't won't be affected by someone sorting their package.json keys (and subkeys).

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. new expression-level syntax)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Won't FixThe severity and priority of this issue do not warrant the time or complexity needed to fix it

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions