Skip to content

Coercion (string→number, string→boolean, etc.)  #228

@yamcodes

Description

@yamcodes

Problem

Environment variables are always strings at runtime, but users want to treat them as typed primitives without manual conversion.

Current state:

// Manual conversion required
const env = arkenv({
  PORT: type("string").pipe(str => Number.parseInt(str, 10)),
  DEBUG: type("string").pipe(str => str === "true")
});

Desired state:

// Coercion
const env = arkenv({
  PORT: "number",           // "3000" → 3000
  DEBUG: "boolean",        // "true" → true  
  TIMESTAMP: "number.epoch" // "1640995200000" → 1640995200000
});

Current Implementation

Since v0.7.3, we coerce booleans by overriding the boolean keyword in our scope. However, this approach has limitations:

  1. Manual per-type: Must override each primitive individually
  2. Root-only: Doesn't work with sub-keywords like number.epoch, number.integer
  3. Not scalable: New ArkType types require manual implementation

Proposed Solution

We are deciding between 2 solutions. Here is the first one we came up with:

  1. Use the input types to understand what's the desired target type (say, number).
  2. If the desired target type requires coercion (for example, is not a direct string or literal, etc.) then convert it using JavaScript.
  3. Bring that back to ArkEnv for the actual validation.

This approach allows us to have full control of the conversion layer, and if we can use some type-foo (or ArkType-foo) to get the right values from ArkType, this can work.

Alternative Solution

  1. Use the input types to understand what's the desired target type (say, number).
  2. Try to add .parse at the end, and see if it compiles.
  3. If it does, use that as the type input in what goes into ArkType.

This work since ArkType actually provides the ability to morph within the type system. Example, it provides "string.date" which simply validates a date-like string, but also string.date.parse which will actually convert the result into a JavaScript Date. we can use that to our advantage to simplify the solution.

The tradeoffs are:

  1. I'm not sure how possible or viable this is, as I'm not too well versed into the ArkType type system.
  2. We "give up" control of the parsing logic towards ArkType, which means if it doesn't have the ability to parse a certain type, we don't, as well.

(1) won't be much of a tradeoff depending on research, and (2) isn't much of a tradeoff because we can always extend the type system with additional types like we already did with number.port and string.host.

It's a potentially much cleaner solution if we can come up with a nice looking PoC

Sub-issues

Metadata

Metadata

Assignees

Labels

arkenvChanges to the `arkenv` npm package.enhancementNew feature or requesthelp wantedExtra attention is needed

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions