Skip to content

[Feature] Replace HoconParser value-type dispatch with match guard #241

@pathosDev

Description

@pathosDev

Size / Priority

  • Size: Trivial (~30 lines)
  • Category: C.1 Pattern-Matching.
  • Risk: low.

Affected files

  • src/config/HoconParser.ts — value-type dispatch in the value-emitting / substitution-resolving path.

Background

HoconParser produces ConfigValue which is a union:

export type ConfigValue = ConfigPrimitive | ConfigValue[] | ConfigObject | Substitution;
export type ConfigPrimitive = string | number | boolean | null;

Several functions in the parser branch on the value's runtime type:

  • resolveSubstitutions(value) — recurses through arrays/objects; resolves Substitution leaves.
  • deepMerge(a, b) — merges objects; replaces primitives.
  • serialize(value) — emit canonical form.

The dispatch typically uses chained typeof + Array.isArray + isSubstitution checks. ts-pattern's P predicates (P.string, P.number, P.boolean, P.nullish, P.array, P.when) give a uniform syntax.

Target code (example for resolveSubstitutions)

import { match, P } from 'ts-pattern';
import { isSubstitution } from './HoconParser.js';

function resolveSubstitutions(value: ConfigValue, root: ConfigObject): ConfigValue {
  return match(value)
    .with(P.string,   (v) => v)
    .with(P.number,   (v) => v)
    .with(P.boolean,  (v) => v)
    .with(P.nullish,  () => null)
    .with(P.array(P.any), (arr) => arr.map(v => resolveSubstitutions(v, root)))
    .with({ __substitution: true }, (sub) => resolveOne(sub, root))
    .otherwise((obj) => {
      // ConfigObject
      const out: ConfigObject = {};
      for (const [k, v] of Object.entries(obj)) out[k] = resolveSubstitutions(v, root);
      return out;
    });
}

.otherwise(...) handles the residual ConfigObject case after exhausting primitives, arrays, and substitutions — exhaustiveness preserved via the match's type inference.

Integration / risk

  • HOCON parsing semantics unchanged.
  • Substitution resolution + deep-merge correctness preserved.

Test plan

  1. Regression — full HOCON parser test suite.
  2. Substitution edge cases — nested substitutions, missing keys with ${?optional}.
  3. Deep-merge tests — multi-source merge order preserved.

Acceptance criteria

  • Value-type dispatch sites use match().with(P.*).
  • HOCON tests pass.
  • No CHANGELOG entry needed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestpriority: lowNice-to-have / niche / demand-driven

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions