Skip to content

Commit

Permalink
Use more strict type for schema (#134)
Browse files Browse the repository at this point in the history
* Use more strict type for schema

* Add type support for third-party json-schema libraries
  • Loading branch information
klaseca authored Sep 28, 2022
1 parent f912c71 commit 3307ab4
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 50 deletions.
27 changes: 22 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ const config = envSchema({
schema: schema,
data: data, // optional, default: process.env
dotenv: true // load .env if it is there, default: false
})
})

// config.data => ['127.0.0.1', '0.0.0.0']
```
Expand Down Expand Up @@ -198,13 +198,13 @@ console.log(config)
You can specify the type of your `config`:

```ts
import envSchema from 'env-schema';
import { envSchema, JSONSchemaType } from 'env-schema'

interface Env {
PORT: number;
}

const schema = {
const schema: JSONSchemaType<Env> = {
type: 'object',
required: [ 'PORT' ],
properties: {
Expand All @@ -215,8 +215,25 @@ const schema = {
}
}

const config = envSchema<Env>({
schema,
const config = envSchema({
schema
})
```

You can also use a `JSON Schema` library like `typebox`:

```ts
import { envSchema } from 'env-schema'
import { Static, Type } from '@sinclair/typebox'

const schema = Type.Object({
PORT: Type.Number({ default: 3000 })
})

type Schema = Static<typeof schema>

const config = envSchema<Schema>({
schema
})
```

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
},
"devDependencies": {
"@fastify/pre-commit": "^2.0.2",
"@sinclair/typebox": "^0.24.43",
"ajv-formats": "^2.1.1",
"fluent-json-schema": "^3.0.0",
"snazzy": "^9.0.0",
Expand Down
28 changes: 17 additions & 11 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import Ajv, { KeywordDefinition } from "ajv";
import { DotenvConfigOptions } from "dotenv";
import Ajv, { KeywordDefinition, JSONSchemaType } from 'ajv';
import { AnySchema } from 'ajv/dist/core';
import { DotenvConfigOptions } from 'dotenv';
import { ObjectSchema } from 'fluent-json-schema';

export type { JSONSchemaType };

export type EnvSchemaData = {
[key: string]: unknown;
};

export type EnvSchemaOpt = {
schema?: object;
export type EnvSchemaOpt<T = EnvSchemaData> = {
schema?: JSONSchemaType<T> | AnySchema | ObjectSchema;
data?: [EnvSchemaData, ...EnvSchemaData[]] | EnvSchemaData;
env?: boolean;
dotenv?: boolean | DotenvConfigOptions;
expandEnv?: boolean
ajv?: Ajv | {
customOptions(ajvInstance: Ajv): Ajv;
};
expandEnv?: boolean;
ajv?:
| Ajv
| {
customOptions(ajvInstance: Ajv): Ajv;
};
};

declare const loadAndValidateEnvironment: {
<T = EnvSchemaData>(_opts?: EnvSchemaOpt): T;
<T = EnvSchemaData>(_opts?: EnvSchemaOpt<T>): T;
keywords: {
separator: KeywordDefinition;
}
}
};
};

export default loadAndValidateEnvironment;
export { loadAndValidateEnvironment as envSchema };
85 changes: 51 additions & 34 deletions types/index.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
import { expectError, expectType } from "tsd";
import envSchema, { EnvSchemaData, EnvSchemaOpt, envSchema as envSchemaNamed, default as envSchemaDefault } from "..";
import Ajv, { KeywordDefinition } from 'ajv'
import { expectError, expectType } from 'tsd';
import envSchema, {
EnvSchemaData,
EnvSchemaOpt,
envSchema as envSchemaNamed,
default as envSchemaDefault,
} from '..';
import Ajv, { KeywordDefinition, JSONSchemaType } from 'ajv';
import { Static, Type } from '@sinclair/typebox';

const schema = {
type: "object",
required: ["PORT"],
interface EnvData {
PORT: number;
}

const schemaWithType: JSONSchemaType<EnvData> = {
type: 'object',
required: ['PORT'],
properties: {
PORT: {
type: "string",
type: 'number',
default: 3000,
},
},
};

const schemaTypebox = Type.Object({
PORT: Type.Number({ default: 3000 }),
});

type SchemaTypebox = Static<typeof schemaTypebox>;

const data = {
foo: "bar",
foo: 'bar',
};

expectType<EnvSchemaData>(envSchema());
Expand All @@ -23,10 +40,15 @@ expectType<EnvSchemaData>(envSchemaDefault());
const emptyOpt: EnvSchemaOpt = {};
expectType<EnvSchemaOpt>(emptyOpt);

const optWithSchema: EnvSchemaOpt = {
schema,
const optWithSchemaTypebox: EnvSchemaOpt = {
schema: schemaTypebox,
};
expectType<EnvSchemaOpt>(optWithSchemaTypebox);

const optWithSchemaWithType: EnvSchemaOpt<EnvData> = {
schema: schemaWithType,
};
expectType<EnvSchemaOpt>(optWithSchema);
expectType<EnvSchemaOpt<EnvData>>(optWithSchemaWithType);

const optWithData: EnvSchemaOpt = {
data,
Expand Down Expand Up @@ -58,37 +80,32 @@ const optWithDotEnvOpt: EnvSchemaOpt = {
expectType<EnvSchemaOpt>(optWithDotEnvOpt);

const optWithEnvExpand: EnvSchemaOpt = {
expandEnv: true
}
expandEnv: true,
};
expectType<EnvSchemaOpt>(optWithEnvExpand);

const optWithAjvInstance: EnvSchemaOpt = {
ajv: new Ajv()
ajv: new Ajv(),
};
expectType<EnvSchemaOpt>(optWithAjvInstance)
expectType<KeywordDefinition>(envSchema.keywords.separator)

expectType<EnvSchemaOpt>(optWithAjvInstance);
expectType<KeywordDefinition>(envSchema.keywords.separator);

const optWithAjvCustomOptions: EnvSchemaOpt = {
ajv: {
customOptions(ajvInstance: Ajv): Ajv {
return new Ajv();
}
}
ajv: {
customOptions(ajvInstance: Ajv): Ajv {
return new Ajv();
},
},
};
expectType<EnvSchemaOpt>(optWithAjvCustomOptions)
expectType<EnvSchemaOpt>(optWithAjvCustomOptions);
expectError<EnvSchemaOpt>({
ajv: {
customOptions(ajvInstance: Ajv) {
}
}
ajv: {
customOptions(ajvInstance: Ajv) {},
},
});

const envSchemaDefaultsToEnvSchemaData = envSchema({ schema: schema });
expectType<EnvSchemaData>(envSchemaDefaultsToEnvSchemaData)
const envSchemaWithType = envSchema({ schema: schemaWithType });
expectType<EnvData>(envSchemaWithType);

interface EnvData {
PORT: string
}
const envSchemaAllowsToSpecifyType = envSchema<EnvData>({ schema });
expectType<EnvData>(envSchemaAllowsToSpecifyType)
const envSchemaTypebox = envSchema<SchemaTypebox>({ schema: schemaTypebox });
expectType<SchemaTypebox>(envSchemaTypebox);

0 comments on commit 3307ab4

Please sign in to comment.