I'm trying to write a generic env field getter function and currently have this:
export interface Config {
  readonly PORT: number;
  readonly DATABASE_URL: string;
  // ... other fields
}
const config: Config = Object.freeze({
  ENVIRONMENT,
  PROJECT_NAME,
  PORT: parseInt(getEnvVariable('PORT', '9000'), 10),
  DATABASE_URL: getEnvVariable('DATABASE_URL'),
  AWS_REGION,
});

function getEnvVariable<T>(name: string, defaultValue?: T): T | string {
  const val = process.env[name];

  if (val) {
    return val;
  }

  if (defaultValue) {
    return defaultValue;
  }

  throw new Error(`Missing environment variable: ${name}`);
}

and I'm currently getting the error:
Type 'Readonly<{ ENVIRONMENT: string; PROJECT_NAME: string; PORT: number; DATABASE_URL: unknown; AWS_REGION: string; }>' is not assignable to type 'Config'.
  Types of property 'DATABASE_URL' are incompatible.
    Type 'unknown' is not assignable to type 'string'.


For some reason if my defaultValue input is undefined, it will give this error, even though I do a truthy check.
How would I fix this without using an '' as the defaultValue?

Typescript returns unknown for generic only on undefined input
If you don't pass in a defaultValue argument to getEnvVariable, then the compiler has no inference site for the generic type parameter T.  So inference fails, and T falls back to its constraint, which is implicitly the unknown type.
If you'd like T to fall back to something else, you can use a default type argument as shown here:
function getEnvVariable<T = string>( 
// default type arg ----> ^^^^^^^^ 
  name: string, defaultValue?: T
): T | string { /* impl */ }

Then you'll get the desired behavior when defaultValue isn't supplied:
const unsuppliedDefault = getEnvVariable("Y");
// const unsuppliedDefault: string

without changing the behavior when it is:
const suppliedDefault = getEnvVariable("X", Math.random());
// const suppliedDefault: string | number

Playground link to code
