I am declaring the following variables using TypeScript:
const BOT_PREFIX: string = process.env.PREFIX;
const BOT_TOKEN: string = process.env.TOKEN;
I get the following error:
Type 'string | undefined' is not assignable to type 'string'. Type 'undefined' is not assignable to type 'string'.ts(2322)
I can fix it by changing the data type to any
, but i don't want to do that.
Node's process.env
has the following type definition (from this declaration file which uses a type defined here):
interface ProcessEnv {
[key: string]: string | undefined
}
So, as far as TypeScript's compiler is concerned, any property you access under process.env
will be of type string | undefined
. That's a union, meaning that process.env.RANDOMKEY
(for example) will either be a string
or it will be undefined
. Generally speaking this is the right type; the compiler has no idea which environment variables are actually set.
And so this is a problem:
const BOT_PREFIX: string = process.env.PREFIX // error!
and the compiler warns you that process.env.PREFIX
might be undefined
, so it's not safe to treat it as a string
.
The way to deal with this depends on whether you want convenience or type safety, and what you want to see happen if your assumptions about process.env.PREFIX
and process.env.TOKEN
are incorrect.
For pure convenience, you probably can't beat the non-null assertion operator (!
):
const BOT_PREFIX: string = process.env.PREFIX!; // okay
const BOT_TOKEN: string = process.env.TOKEN!; // okay
Here you are just telling the compiler that, even though it cannot verify this, you are sure process.env.PREFIX
and process.env.TOKEN
will be defined. This essentially just suppresses the compiler warning; it's still doing the same thing at runtime as your original code. And that means if you turn out to be wrong about your assertion, then you might run into problems at runtime that the compiler can't help you with:
BOT_PREFIX.toUpperCase(); // runtime error if BOT_PREFIX is undefined after all
So be careful.
On the other hand, you can try to make the code safer by handling the situation in which the environment variables you expect are not set. For example:
function getEnvVar(v: string): string {
const ret = process.env[v];
if (ret === undefined) {
throw new Error("process.env." + v + " is undefined!");
}
return ret;
}
const BOT_PREFIX: string = getEnvVar("PREFIX");
const BOT_TOKEN: string = getEnvVar("TOKEN");
Here we've written a function called getEnvVar()
which takes the name of the environment variable and returns its value, as long as that value is a string
. If the environment variable is not defined, then a runtime error will be thrown. TypeScript understands via control flow analysis that the return type of getEnvVar()
is string
(and not string | undefined
; the undefined
possibility has been eliminated by the throw
statement), and so you can safely assign the returned value to a variable of type string
.
This code is obviously less convenient since it requires extra runtime code, but now instead of possibly lying to the compiler and having bizarre runtime behavior, you will get some immediate feedback at runtime if your assumptions are invalid.
Playground link to code
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With