[Feature Request] Discriminating unions for generated types

Open
#850 1 comment 0 reactions 1 assignee View on GitHub

Maintainers usually reply within 1 day

@theoephraim is already working on this.

Since Jul 1, 2026.

Assessment

This issue has not been assessed yet.

Description

enhancement
Description

When a variable's "optionality" (probably not a word - sorry) depends on some other variable, like currentEnv, it would be really convenient if the generated types would reflect this.

In its simplest form, it would be something like this:

# @generateTypes(lang=ts, path=varlock.d.ts)
# @currentEnv=$APP_ENV
# ----------

# @type=enum(dev, staging, prod, building)
# @sensitive={preventLeaks=false}
APP_ENV=dev

# @optional=forEnv(building)
# @sensitive
DATABASE_PASSWORD=

Allowing the user to then make the following checks / assertions:

import { ENV } from 'varlock/env';

if (ENV.APP_ENV !== 'building') {
  // in this scope, `ENV.DATABASE_PASSWORD` is of type `string`, and not `string | undefined`.
}
Motivation

The advantage of using varlock for us is not only that we can fetch secrets from one of the plugins before an application is initialized. It is also the fact that variables can depend on other variables, and generally the ability to script the the environment using the various resolver functions.

As we do not want to bake secrets into our docker containers, we have marked nearly every variable as @sensitive (with those that are actually public as @sensitive={preventLeaks=false} - a hack while waiting for #750 - although we cannot import this client-side, but that's outside the scope of this request).

A lot of our server side code needs to be initialized once the container is up and running, thus we a few checks like the one above in our codebase.
Since typescript solves this with discriminated unions, I would be very happy to see the implemented in varlock as well.

Proposed Solution

Generate the type declaration file along the following lines:

type CoercedEnvSchema
  = | {
    APP_ENV: 'dev' | 'staging' | 'prod';
    DATABASE_PASSWORD: string;
  } | {
    APP_ENV: 'building';
    DATABASE_PASSWORD?: string;
  };

This is of course simplified - it would probably make sense to destructure the type definition into multiple types, as the complexity increases.

Alternatives

Using the "Non-null assertion" operator (!) on all the affected variables.

Creating a .env.building file, filled with mock variables has not really been a consideration - because that could lead misuse of the DATABASE_PASSWORD variable - trying to instantiate a database connection, when the variable is actually nonsense, making the application throw on build or similar.
With the non-null assertion check the developer manually checks that the environment is set properly.

Additional Information

I get that the complexity of this task quickly becomes huge, as the resolver functions can return more or less anything, so I would suggest limiting the functionality to begin with.
Like only supporting forEnv() in the @optional / @required decorators (and maybe other variables having @type=boolean @required decorators), when generating these type unions.

I would also be willing to assist in coming up with a proper algorithm for generating these types correctly.

Dominant language
TypeScript
Stars
4.9k
Forks
140
Avg merge
2d 3h
Merged PRs (30d)
35

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from dmno-dev/varlock

All issues in dmno-dev/varlock

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.