Export type not matching variable type

I have a TypeScript project with a file that loads env variables and export it:

const.ts:

const {
  VARIABLE0, // type of VARIABLE0 is string | undefined
  VARIABLE1,
} = process.env;

if (!VARIABLE0 || !VARIABLE1) {
  throw new Error('Invalid env');
}

console.log(VARIABLE0); // type of VARIABLE0 is string
console.log(VARIABLE0.length);

export {
  VARIABLE0, // type VARIABLE0 is string | undefined
  VARIABLE1,
};

I don’t understand why the exported type is string | undefined because before, I check that it is defined. On VS Code If I hover on VARIABLE0 after the check, it is string but if I hover on the export, it is string | undefined. It is anoying on other part of my code that uses this variables because I have to add ? and ! in many places (for instance const length = VARIABLE0?.length! instead of const length = VARIABLE0.length).

Is there a way to export it as string and not string | undefined?

One way I see is:

const {
  VARIABLE0: VARIABLE0_TEMP, VARIABLE1: VARIABLE1_TEMP,
} = process.env;

if (!VARIABLE0_TEMP || !VARIABLE1_TEMP) {
  throw new Error('Invalid env');
}

const VARIABLE0 = <string>VARIABLE0_TEMP;
const VARIABLE1 = <string>VARIABLE1_TEMP;

export {
  VARIABLE0, VARIABLE1,
};

But it’s probably not the best solution (it is quite verbose and I actually have around 10 variables to export like this).

>Solution :

One way to do it is to use an assertion function to narrow process.env. For example:

function validateConfig<T extends string>(
    env: NodeJS.ProcessEnv,
    required: readonly T[],  // readonly allows arg to be const-asserted
): asserts env is NodeJS.ProcessEnv & Record<T, string> {
    const missing = required.filter((envVar) => !(envVar in env));
    if (missing.length > 0) {
        throw new Error(`missing required env vars: ${missing.join(", ")}`);
    }
}

Now you can use this like:

validateConfig(process.env, [
    "VARIABLE0",
    "VARIABLE1",
]);

and either the error gets thrown or process.env.VARIABLE0 and process.env.VARIABLE1 are both string, so you can then e.g.:

const { VARIABLE0, VARIABLE1 } = process.env;

export {
  VARIABLE0, // type VARIABLE0 is string
  VARIABLE1,
};

Playground

Note that if the required argument is typed as string[] the result will be NodeJS.ProcessEnv & Record<string, string>, i.e. it tells the compiler that every possible environment variable will exist – either inline the list of required variables or type it as const to get the narrower string union array/tuple type.

Leave a Reply