-
Notifications
You must be signed in to change notification settings - Fork 3
Description
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:
- Manual per-type: Must override each primitive individually
- Root-only: Doesn't work with sub-keywords like
number.epoch,number.integer - 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:
- Use the input types to understand what's the desired target type (say,
number). - If the desired target type requires coercion (for example, is not a direct string or literal, etc.) then convert it using JavaScript.
- 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
- Use the input types to understand what's the desired target type (say,
number). - Try to add
.parseat the end, and see if it compiles. - 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:
- I'm not sure how possible or viable this is, as I'm not too well versed into the ArkType type system.
- 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