Hone logo
Hone
Problems

Implement a TypeScript Semantic Versioning Type System

This challenge requires you to design and implement a robust TypeScript type system for semantic versioning (SemVer). A well-defined SemVer type system will improve code quality, catch common errors at compile time, and enhance developer understanding of version compatibility.

Problem Description

You need to create a set of TypeScript types that accurately represent and enforce the rules of Semantic Versioning (SemVer) as defined by the SemVer 2.0.0 specification. This includes defining types for:

  1. Core Version Components: Major, Minor, and Patch versions, which must be non-negative integers.
  2. Pre-release Identifiers: Optional identifiers like alpha, beta, rc, followed by numbers.
  3. Build Metadata: Optional identifiers that do not affect version precedence.
  4. Valid SemVer Strings: A type that can represent any valid SemVer string and can be parsed into its constituent parts.
  5. Version Comparison: Mechanisms to compare two SemVer versions and determine their precedence.

Key Requirements:

  • Type Safety: Ensure that invalid SemVer combinations are caught by the TypeScript compiler.
  • Immutability: Versions should be treated as immutable once created.
  • Parsing: A function to parse a string into a structured SemVer object.
  • Stringification: A function to convert a structured SemVer object back into its canonical string representation.
  • Precedence Comparison: A function to compare two SemVer versions and return an indicator of their relationship (e.g., less than, equal to, greater than).
  • Clear Type Definitions: Define distinct types for different parts of a SemVer version (e.g., MajorVersion, MinorVersion, PatchVersion, PreReleaseIdentifier, BuildMetadataIdentifier).

Expected Behavior:

  • A valid SemVer string should be parsable without errors.
  • An invalid SemVer string should either fail to parse or be rejected by the type system.
  • The precedence comparison should follow the SemVer 2.0.0 rules precisely.
  • TypeScript should prevent nonsensical assignments, e.g., assigning a string that doesn't conform to SemVer to a type expecting a SemVer object.

Edge Cases:

  • Versions with no pre-release or build metadata.
  • Versions with single-digit and multi-digit pre-release identifiers.
  • Leading zeros in numeric identifiers (SemVer disallows this for core versions, but it's important to handle or reject).
  • Empty pre-release or build metadata identifiers.

Examples

Example 1: Basic Version Parsing and Stringification

// Input
const versionString = "1.2.3";

// Expected Output (conceptually, based on your type definitions)
// {
//   major: 1,
//   minor: 2,
//   patch: 3,
//   preRelease: [],
//   build: []
// }
// Parsed string: "1.2.3"

// Explanation: A simple version string with no pre-release or build metadata is parsed into its core components and stringified back to its original form.

Example 2: Version with Pre-release and Build Metadata

// Input
const versionString = "2.0.0-alpha.1+build.123";

// Expected Output (conceptually)
// {
//   major: 2,
//   minor: 0,
//   patch: 0,
//   preRelease: ["alpha", 1],
//   build: ["build", 123]
// }
// Parsed string: "2.0.0-alpha.1+build.123"

// Explanation: A more complex version string including pre-release identifiers and build metadata is correctly parsed and stringified.

Example 3: Version Precedence Comparison

// Input
const versionA = "1.0.0";
const versionB = "1.0.0-alpha.1";
const versionC = "1.0.1";
const versionD = "2.0.0";

// Expected Output (conceptually for comparison functions)
// compare(versionA, versionB) -> greater than (1)
// compare(versionB, versionA) -> less than (-1)
// compare(versionA, versionA) -> equal to (0)
// compare(versionA, versionC) -> less than (-1)
// compare(versionC, versionD) -> less than (-1)

// Explanation: Demonstrates how different versions are ordered according to SemVer precedence rules. Pre-release versions are lower than normal versions.

Constraints

  • All core version components (major, minor, patch) must be non-negative integers.
  • Pre-release and build metadata identifiers can be alphanumeric strings or numbers.
  • Numeric identifiers within pre-release and build metadata should be treated as numbers where appropriate for precedence.
  • Leading zeros in numeric identifiers for major, minor, and patch versions are invalid SemVer and should be rejected or handled according to strict SemVer parsing.
  • Your solution must be implementable entirely within TypeScript, leveraging its type system features.
  • Performance is not a primary concern for this challenge, but overly inefficient parsing or comparison logic should be avoided.

Notes

  • Refer to the SemVer 2.0.0 specification for detailed rules on versioning, pre-release identifiers, build metadata, and precedence.
  • Consider using branded types or intersection types in TypeScript to enforce SemVer constraints at the type level.
  • Think about how you will represent the different parts of a SemVer version (e.g., using union types, discriminated unions, or interfaces).
  • The challenge is to build the type system and the associated runtime logic to support it. You should aim for a solution where the types themselves provide significant compile-time validation.
Loading editor...
typescript