TypeScript Peer Dependency Resolver
This challenge focuses on defining robust TypeScript types for managing peer dependencies, a common pattern in JavaScript package management. You'll need to create types that accurately represent the structure of package.json's peerDependencies and implement a way to infer or validate the compatibility between different package versions.
Problem Description
Your task is to define TypeScript types that model the peerDependencies section of a package.json file. These types should be flexible enough to handle simple version ranges (e.g., "^1.0.0", ">=2.0.0 <3.0.0") and potentially more complex requirements.
Furthermore, you need to design a mechanism to check if a set of installed packages (represented by their versions) satisfies the peer dependency requirements of a given package. This is crucial for package managers to warn users when incompatible versions are installed.
Key Requirements:
PeerDependencyType: Define a type that represents a single peer dependency, including its name and version requirement.PackageJsonType: Define a type that represents the relevant parts of apackage.jsonfile, specifically focusing onpeerDependencies.VersionRangeType (Optional but Recommended): Consider how to represent and potentially parse/validate version ranges (e.g., SemVer). For this challenge, you can assume a string representation is sufficient if a full parsing implementation is too complex.ResolverFunction/Type: Create a type or function that takes a target package's peer dependencies and a list of installed package versions, and determines if the requirements are met.
Expected Behavior:
- The types should accurately reflect the structure of
peerDependencies. - The resolver should correctly identify when a set of installed packages meets or fails to meet the peer dependency requirements.
- The system should handle cases where a required peer dependency is missing or an incompatible version is present.
Edge Cases to Consider:
- A package with no peer dependencies.
- A package that requires a specific version without a range (e.g.,
"1.2.3"). - A package that requires a minimum version (e.g.,
">=2.0.0"). - A package that requires a maximum version (e.g.,
"<3.0.0"). - A package that requires a version range with both minimum and maximum (e.g.,
">=2.0.0 <3.0.0"). - The case where a required peer dependency is not installed.
Examples
Example 1:
// Assume we have a simplified version parsing utility (or we'll handle strings directly)
// For simplicity, let's assume string matching for basic checks.
// Input:
interface InstalledPackage {
name: string;
version: string;
}
const reactPackageJson = {
name: "react",
peerDependencies: {
"object-assign": "^4.0.0",
},
};
const installedPackages: InstalledPackage[] = [
{ name: "object-assign", version: "4.1.1" },
{ name: "scheduler", version: "0.23.0" },
];
// Output:
// Expected: true (because "object-assign": "4.1.1" satisfies "^4.0.0")
Example 2:
// Input:
const myComponentPackageJson = {
name: "my-component",
peerDependencies: {
react: "^17.0.0",
"react-dom": "^17.0.0",
},
};
const installedPackages: InstalledPackage[] = [
{ name: "react", version: "18.2.0" }, // This might be considered compatible depending on exact SemVer logic
{ name: "react-dom", version: "17.0.2" },
];
// Output:
// Expected: true (if "^17.0.0" is understood to include versions like "18.2.0" in certain contexts,
// or false if strict adherence to the specified major version is required. Let's assume strict for now, so false)
// For this challenge, let's refine our interpretation: "^17.0.0" should match "17.x.x" but not "18.x.x".
// So, in this example, the output should be `false` because react "18.2.0" does not satisfy "^17.0.0".
// Let's refine the example based on stricter SemVer interpretation:
const myComponentPackageJsonStrict = {
name: "my-component",
peerDependencies: {
react: "^17.0.0", // Requires a version that starts with 17.x.x
"react-dom": "^17.0.0",
},
};
const installedPackagesStrict: InstalledPackage[] = [
{ name: "react", version: "18.2.0" }, // Does NOT satisfy "^17.0.0"
{ name: "react-dom", version: "17.0.2" },
];
// Output:
// Expected: false (because 'react' version 18.2.0 does not satisfy "^17.0.0")
Example 3 (Edge Case - Missing Dependency):
// Input:
const advancedComponentPackageJson = {
name: "advanced-component",
peerDependencies: {
"lodash": "^4.17.0",
"moment": "2.29.0",
},
};
const installedPackages: InstalledPackage[] = [
{ name: "lodash", version: "4.17.21" },
// 'moment' is missing
];
// Output:
// Expected: false (because 'moment' is a required peer dependency and is not installed)
Constraints
- Version String Format: Assume versions follow Semantic Versioning (SemVer) conventions (e.g.,
MAJOR.MINOR.PATCH). You do not need to implement a full SemVer parser, but your types and logic should be compatible with standard SemVer ranges. - Input Data Structure:
package.json'speerDependenciesis an object where keys are package names and values are version specifiers (strings). Installed packages will be provided as an array of objects withnameandversionproperties. - Performance: For this exercise, performance is not a primary concern, but overly inefficient solutions might be flagged. Focus on correctness and type safety.
- Scope: You are only concerned with defining the types and the resolution logic. You do not need to implement file reading or actual
package.jsonparsing from disk.
Notes
- Consider how you will represent the version specifiers. Will you stick to strings, or will you define specific types for common range operators (
^,~,>=,<, etc.)? For this challenge, you can start by treating them as strings and defining a simple logic for common prefixes like^and>=. - Think about how to map installed package versions to the
peerDependenciesthey are fulfilling. - The core of the challenge lies in creating a type-safe and robust way to express these relationships.
- A good solution will use TypeScript's features effectively to prevent runtime errors and provide clear type definitions.