Peer Dependency Resolution in TypeScript
Managing peer dependencies can be tricky, especially when dealing with multiple versions of the same dependency across different packages within a monorepo or multi-package project. This challenge asks you to create TypeScript types that help enforce and resolve peer dependency constraints, ensuring compatibility and preventing version conflicts. This is crucial for maintaining a stable and predictable development environment.
Problem Description
You need to define TypeScript types that represent and resolve peer dependency relationships. The core of the challenge is to create a type that, given a package's peer dependencies and the versions of those dependencies installed in its environment, determines the compatible versions of the peer dependencies. The resolver should handle version ranges (e.g., "1.x.x", ">=2.0.0", "<3.0.0") and return a set of compatible versions.
Key Requirements:
PeerDependencyType: Define a type representing a single peer dependency. This type should include the package name (string) and a version range (string). The version range should follow semantic versioning (semver) conventions.PeerDependenciesType: Define a type representing a collection of peer dependencies. This should be a mapping of package name to version range (e.g.,{ "react": "16.x.x", "lodash": ">=4.0.0" }).InstalledVersionType: Define a type representing a specific installed version of a package (string).InstalledVersionsType: Define a type representing a collection of installed versions for different packages (e.g.,{ "react": "16.8.0", "lodash": "4.17.21" }).ResolvePeerDependenciesType: Define a function type that takes aPeerDependenciesobject and anInstalledVersionsobject as input and returns anInstalledVersionsobject. The returned object should contain only the peer dependencies that are actually present in the installed versions. If a peer dependency is not installed, it should not be included in the resolved output.- Semver Compatibility: The
ResolvePeerDependenciesfunction must correctly determine if an installed version satisfies the version range specified in the peer dependency. You can use a library likesemverfor this purpose (install it withnpm install semver).
Expected Behavior:
The ResolvePeerDependencies function should filter the peer dependencies, keeping only those that have a matching installed version. It should not modify the installed versions themselves; it should only return a subset of them.
Edge Cases to Consider:
- Empty
PeerDependenciesobject. - Empty
InstalledVersionsobject. - Version ranges that are very broad (e.g., "x.x.x").
- Version ranges that are very specific (e.g., "1.2.3").
- Packages with no peer dependencies.
- Packages with conflicting peer dependencies (e.g., one peer dependency requiring "react@16.x.x" and another requiring "react@17.x.x"). The resolver should simply return the compatible versions it finds, without attempting to resolve the conflict.
Examples
Example 1:
const peerDependencies: PeerDependencies = {
react: "16.x.x",
lodash: ">=4.0.0"
};
const installedVersions: InstalledVersions = {
react: "16.8.0",
lodash: "4.17.21",
express: "4.17.1" // Not a peer dependency
};
// Expected Output:
// { react: "16.8.0", lodash: "4.17.21" }
Explanation: Both react and lodash are peer dependencies and have compatible installed versions. express is not a peer dependency and is therefore excluded.
Example 2:
const peerDependencies: PeerDependencies = {
react: "17.x.x"
};
const installedVersions: InstalledVersions = {
react: "16.8.0"
};
// Expected Output:
// {}
Explanation: react is a peer dependency, but the installed version ("16.8.0") does not satisfy the version range "17.x.x". Therefore, the output is an empty object.
Example 3:
const peerDependencies: PeerDependencies = {
react: ">=16.0.0 <17.0.0"
};
const installedVersions: InstalledVersions = {
react: "16.9.0",
react: "16.5.0" // Duplicate key, only the last value matters
};
// Expected Output:
// { react: "16.9.0" }
Explanation: The version range allows versions between 16.0.0 (inclusive) and 17.0.0 (exclusive). "16.9.0" satisfies this range, while "16.5.0" also does. The last installed version for a key is used.
Constraints
- The version range format should adhere to semantic versioning (semver) conventions.
- You must use the
semverlibrary for version comparison. - The solution must be written in TypeScript.
- The
ResolvePeerDependenciesfunction should be efficient enough to handle a reasonable number of peer dependencies (e.g., up to 100).
Notes
- Consider using generics to make your types more flexible.
- Think about how to handle different types of version ranges (e.g., exact versions, version ranges, wildcards).
- The focus is on the type definitions and the logic for resolving peer dependencies, not on error handling or complex build tooling. Assume the input data is valid.
- The
InstalledVersionstype should only contain keys that are also present in thePeerDependenciesobject.