question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Allow peer dependencies with stricter version ranges

See original GitHub issue

Continuation of https://github.com/microsoft/types-publisher/issues/431.

Current state

When you import from a package hosted in the DefinitelyTyped repository in a package that’s also hosted in DefinitelyTyped, the types publisher will automatically declare a direct dependency on that types package matching any version. For example, if you have a package react-library that imports from react then the package.json of @types/react-library will look something like this:

{
  "name": "@types/react-library",
  "dependencies": {
    "@types/react": "*"
  }
}

Problems

Combination of the described problems are encountered in

duplication of @types/* packages

Given an existing module graph similar to

types-peer-deps@1.0.0 /home/eps1lon/Development/throwaway/types-peer-deps
├─┬ @types/react-library@1.0.0
│ └── @types/react@17.0.44 deduped
└── @types/react@17.0.44

If you want to upgrade to React 18 you would need to update @types/react with npm install @types/react@18. However, npm (and yarn and probably other package managers) make sure that this install does not alter the dependency tree too much (if you’re using lockfiles). What you end up with is something like

types-peer-deps@1.0.0 /home/eps1lon/Development/throwaway/types-peer-deps
├─┬ @types/react-library@1.0.0
│ └── @types/react@17.0.44 
└── @types/react@18.0.0

Now your dependency tree contains multiple version of a types package. In this specific case these packages are incompatible. To fix, one would have to apply one of the workarounds described in https://github.com/facebook/react/issues/24304#issuecomment-1094565891 which isn’t trivial at all. After all, package managers are supposed to resolve these issues. But they can’t if we provide them with inaccurate information.

* is not forwards compatible

While currently published packages might work perfectly fine with all published versions of @types/react at the time, there’s no guarantee that continues to work. It essentially requires that no breaking changes are made at all which defeats the purpose of SemVer major releases.

Proposal

Allow peer dependencies

React is generally a peer dependency and therefore it types should as well. If we could move @types/react from dependencies to peer dependencies with

"peerDependencies": { 
  "@types/react": "*"
},

we would ensure types deduplication since the package owner would defines the version of @types/react a single time (via dependencies or devDependency.

Allow constraining @types/* dependencies to something stricter than *

Either let maintainers decide the range manually or infer the range from the versions it’s currently tested on. For example, @types/react-library runs tests with @types/react v15, v16, v17 and latest. In that case we check what version the current release has (or will be) and declare that. This way an update to @types/react-library will also bump versions of dependencies and therefore propagate bug fixes and features of those dependencies upon release.

related issues

Issue Analytics

  • State:open
  • Created a year ago
  • Reactions:17
  • Comments:22 (21 by maintainers)

github_iconTop GitHub Comments

1reaction
fbarthocommented, Apr 13, 2022

DefinitelyTyped is a clever service for the TypeScript Ecosystem. The real “correct” fix is for types to be shipped by the official runtime-packages instead of being provided by DefinitelyTyped. Then they would be versioned exactly like the packages they belong to.

In practice, many DefinitelyTyped definitions are maintained by external communities, and aren’t necessarily shipped simultaneously with their source-packages, there are always going to be warts and weak points. Starting with *-versions was a good compromise initially, because many types packages really don’t care what version, or rarely have breaking changes. Unfortunately, that heuristic has failed today with a popular package that had breaking changes, and so *-package deps didn’t work.

There’s no point throwing the baby out with the bathwater. Let’s refocus this ticket on the discussion of if “peerDependencies with stricter version ranges” is a valid enhancement to DefinitelyTyped capabilities that is worth the cost, and if there are obvious traps that we will fall into.

An additional point to discuss is the impact if “all packages” follow this recommendation. Do we lead to excess thrash on DT’s repo? (Because people have to make new versions to increase the upper bound every time the source package updates?) Other problems?

1reaction
weswighamcommented, Apr 13, 2022

Some thinking here is clearly off - if your types depend on react’s types, the dependency is in no way optional, it’s mandatory. An “optional” peer dep would only serve to break consumers of the package’s types, since they’d suddenly be missing required type information. I can understand wanting to elevate from a dependency to a peer dependency, since people are used to react itself being a peer dep, but all that actually does for types is elevate a (potential! not guaranteed) compile time type conflict to an install time peer dep version conflict - the underlying conflict still exists.

Moreover, all of DT is tested to be internally consistent, so the latest versions of all packages on DT should always work together - enumerating how far back in time, and for what version ranges, that holds is practically impossible over a shifting set of dependencies, hence the * version ranges to allow any version to be installed (thus, deferring to package manager power users in case of esoteric mix-old-with-new dependency relationships). On top of that, using * versions allows us to avoid republishing all dependents of a package when it updates just to update the version strings - which we would always need to do, since, again, DT is kept internally consistent at the latest version of every package.

What’s odd, to me, is that while a package manage is willing to update the outermost package in its tree, why it would be unwilling to update the inner one even though the version specifier allows it? Especially if it already deduped the versions to a single copy? (The package manager seems like it would have to do more work to move the old install to a new location!) Minimally, I know if you blow away your lockfile and install again, they’ll unify to the single valid top-level install again, which is decidedly not what would be the case of version ranges were stricter - you’d just be guaranteed to have the version conflict.

Read more comments on GitHub >

github_iconTop Results From Across the Web

npm Peer Dependencies - Fathom
When specifying the allowed versions of a package in a peer dependency in our package.json , we often want to specify more liberal...
Read more >
NPM peerDependencies how to specify major version range
this command with --force, or --legacy-peer-deps npm ERR! to accept an incorrect (and potentially broken) dependency resolution. The question is.
Read more >
package.json
Dependencies are specified in a simple object that maps a package name to a version range. The version range is a string which...
Read more >
validate-peer-dependencies | Yarn - Package Manager
Usage · find the nearest package. · read that package. · ensure that each of the specified peerDependencies are present and that the...
Read more >
Managing Dependencies
Exact version ( 1.2.3 ) — the most strict, only manual updates. Range ( >=1.3.0 <2.0.0 ) - defines a range of versions....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found