envalid
Version:
Validation for your environment variables
148 lines (127 loc) • 4.98 kB
text/typescript
export interface Spec<T> {
/**
* An Array that lists the admissable parsed values for the env var.
*/
choices?: ReadonlyArray<T>
/**
* A string that describes the env var.
*/
desc?: string
/**
* An example value for the env var.
*/
example?: string
/**
* A url that leads to more detailed documentation about the env var.
*/
docs?: string
/**
* A fallback value, which will be used if the env var wasn't specified. Providing a default effectively makes the env var optional.
*/
default?: NonNullable<T> | undefined
/**
* A fallback value to use only when NODE_ENV is not 'production'.
* This is handy for env vars that are required for production environments, but optional for development and testing.
*/
devDefault?: NonNullable<T> | undefined
}
type OptionalAttrs<T> =
| { default: undefined }
| { devDefault: undefined }
| { default: undefined; devDefault: undefined }
| { default: NonNullable<T>; devDefault: undefined }
| { default: undefined; devDefault: NonNullable<T> }
type RequiredAttrs<T> =
| { default: NonNullable<T> }
| { devDefault: NonNullable<T> }
| { devDefault: NonNullable<T>; default: NonNullable<T> }
| {}
type DefaultKeys = 'default' | 'devDefault'
type OptionalSpec<T> = Spec<T> & OptionalAttrs<T>
type OptionalTypelessSpec = Omit<OptionalSpec<unknown>, 'choices'>
type RequiredSpec<T> = Spec<T> & RequiredAttrs<T>
type RequiredTypelessSpec = Omit<RequiredSpec<unknown>, 'choices' | DefaultKeys>
type ChoicelessOptionalSpec<T> = Omit<Spec<T>, 'choices' | DefaultKeys> & OptionalAttrs<T>
type ChoicelessRequiredSpec<T> = Omit<Spec<T>, 'choices' | DefaultKeys> & RequiredAttrs<T>
type WithParser<T> = {
_parse: (input: string) => T
}
export type RequiredValidatorSpec<T> = RequiredSpec<T> & WithParser<T>
export type OptionalValidatorSpec<T> = OptionalSpec<T> & WithParser<T>
export type ValidatorSpec<T> = RequiredValidatorSpec<T> | OptionalValidatorSpec<T>
// Such validator works for exactly one type. You can't parametrize
// the output type at invocation site (e.g.: boolean).
export interface ExactValidator<T> {
(spec: OptionalSpec<T>): OptionalValidatorSpec<T>
(spec?: RequiredSpec<T>): RequiredValidatorSpec<T>
}
// Such validator only works for subtypes of BaseT.
export interface BaseValidator<BaseT> {
// These function overloads enable nuanced type inferences for optimal DX
// This will prevent specifying "default" alone from narrowing down output type.
// https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads
<T extends BaseT>(spec: OptionalSpec<T>): OptionalValidatorSpec<T>
(spec: ChoicelessRequiredSpec<BaseT>): RequiredValidatorSpec<BaseT>
<T extends BaseT>(spec?: RequiredSpec<T>): RequiredValidatorSpec<T>
}
// Such validator inputs a structured input format such as JSON.
// Because it can output complex types, including objects:
// - it has no supertype
// - it fallbacks to 'any' when no type information can be inferred
// from the spec object.
// - One can't pass "choices" since choices uses reference equality.
export interface StructuredValidator {
// Defaults to any when no argument (prevents 'unknown')
(): RequiredValidatorSpec<any>
// Allow overriding output type with type parameter
<T>(): RequiredValidatorSpec<T>
// Make sure we grab 'any' when no type inference can be made
// otherwise it would resolve to 'unknown'
(spec: RequiredTypelessSpec): RequiredValidatorSpec<any>
(spec: OptionalTypelessSpec): OptionalValidatorSpec<any>
<T>(spec: ChoicelessOptionalSpec<T>): OptionalValidatorSpec<T>
<T>(spec: ChoicelessRequiredSpec<T>): RequiredValidatorSpec<T>
}
export type SpecsOutput<S> = {
[K in keyof S]: unknown
}
export type CleanedEnv<S> = S extends Record<string, ValidatorSpec<unknown>>
? Readonly<
{
[K in keyof S]: S[K] extends OptionalValidatorSpec<infer U>
? U | undefined
: S[K] extends RequiredValidatorSpec<infer U>
? U
: never
} & CleanedEnvAccessors
>
: never
export interface CleanedEnvAccessors {
/** true if NODE_ENV === 'development' */
readonly isDevelopment: boolean
readonly isDev: boolean
/** true if NODE_ENV === 'test' */
readonly isTest: boolean
/** true if NODE_ENV === 'production' */
readonly isProduction: boolean
readonly isProd: boolean
}
export interface ReporterOptions<T> {
errors: Partial<Record<keyof T, Error>>
env: unknown
}
export interface CleanOptions<T> {
/**
* Pass in a function to override the default error handling and console output.
* See ./reporter.ts for the default implementation.
*/
reporter?: ((opts: ReporterOptions<T>) => void) | null
}
export interface StrictProxyMiddlewareOptions {
/**
* A list of extra inspectable properties to add to the middleware.
*
* This is useful if you want to add support for framework-specific values.
*/
extraInspectables?: string[]
}